Eirik Langholm Vullum PRO
JavaScript fanatic that loves node.js and React.
const MyComponent = React.createClass({
render() {
return (
<div>
<h1>{this.props.title}</h1>
<p>{this.props.summary}</p>
</div>
);
}
});
function MyComponent(props) {
return (
<div>
<h1>{props.title}</h1>
<p>{props.summary}</p>
</div>
);
};
function MyComponent(props) {
return (
div({},
h1({}, props.title),
p({}, props.summary)
)
);
};
const description = (
<MyComponent
title="Hello"
summary="World"
/>
);
const description = MyComponent({
title: 'Hello',
summary: 'World'
});
{
type: 'div',
props: {
children: [{
type: 'h1',
props: {
children: 'Hello'
}
}, {
type: 'p',
props: {
children: 'World'
}
}]
}
}
/**
* runtime for DOM
*/
ReactDOM.render(description, mountNode);
/**
* runtime for server/strings
*/
ReactServer.renderToString(description);
function testCase() {
const description = MyComponent({
title: 'Hello',
summary: 'World'
});
}
function testCase() {
const description = MyComponent({
title: 'Hello',
summary: 'World'
});
expect(description).toEqual({
type: 'div',
props: {
children: [{
type: 'h1',
props: {
children: 'Hello'
}
}, {
type: 'p',
props: {
children: 'World'
}
}]
}
})
}
function testCase() {
}
function testCase() {
const description = (
<MyComponent
title="Hello"
summary="World"
/>
);
}
function testCase() {
const description = (
<MyComponent
title="Hello"
summary="World"
/>
);
expect(description).toEqual(
<div>
<h1>Hello</h1>
<p>World</p>
</div>
);
}
function testCase() {
}
/**
* Description
*/
const action = {
type: 'INCREMENT'
};
/**
* Decide how to handle
* descriptions of actions
*/
function reducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
}
/**
* Creating a runtime
*/
const store = createStore(reducer);
/**
* Creating a runtime
*/
const store = createStore(reducer);
/**
* Passing a description
* to the runtime to handle it
*/
store.dispatch(action);
function testCase() {
}
function testCase() {
const initialState = 10;
}
function testCase() {
const initialState = 10;
const actions = [
{ type: 'INCREMENT' },
{ type: 'INCREMENT' },
{ type: 'INCREMENT' },
];
}
function testCase() {
const initialState = 10;
const actions = [
{ type: 'INCREMENT' },
{ type: 'INCREMENT' },
{ type: 'INCREMENT' },
];
const nextState = actions.reduce(reducer, initialState);
}
function testCase() {
const initialState = 10;
const actions = [
{ type: 'INCREMENT' },
{ type: 'INCREMENT' },
{ type: 'INCREMENT' },
];
const nextState = actions.reduce(reducer, initialState);
expect(nextState).toEqual(13);
}
/**
* Async action creator (thunk)
*/
function populateTodos() {
return (dispatch) => {
dispatch(populateTodosPending());
return todoApi.getTodos()
.then((todos) => dispatch(populateTodosSuccess({ todos })))
.catch((error) => dispatch(populateTodosError({ error })));
}
}
/**
* Async action creator (thunk)
*/
function populateTodos() {
return (dispatch) => {
dispatch(populateTodosPending()); // side effect
return todoApi.getTodos()
.then((todos) => dispatch(populateTodosSuccess({ todos })))
.catch((error) => dispatch(populateTodosError({ error })));
}
}
/**
* Async action creator (thunk)
*/
function populateTodos() {
return (dispatch) => {
dispatch(populateTodosPending()); // side effect
return todoApi.getTodos() // side effect
.then((todos) => dispatch(populateTodosSuccess({ todos })))
.catch((error) => dispatch(populateTodosError({ error })));
}
}
/**
* A more general example
*/
function procedure() {
const a = Math.random();
console.log(a);
if (a < 0.5) {
console.log('low');
} else {
console.log('high');
}
return request(`/url/${a}`)
.then((data) => writeToFile(`${a}.txt`, data))
.catch((error) => console.log(error));
}
/**
* A more general example
*/
function procedure() {
const a = Math.random(); // side-effect
console.log(a); // side-effect
if (a < 0.5) {
console.log('low'); // side-effect
} else {
console.log('high'); // side-effect
}
return request(`/url/${a}`) // side-effect
.then((data) => writeToFile(`${a}.txt`, data)) // side-effect
.catch((error) => console.log(error)); // side-effect
}
function procedure() {
const a = Math.random();
console.log(a);
if (a < 0.5) {
console.log('low');
} else {
console.log('high');
}
return request(`/url/${a}`)
.then((data) => writeToFile(`${a}.txt`, data))
.catch((error) => console.log(error));
}
function procedure() {
const a = Math.random();
console.log(a);
if (a < 0.5) {
console.log('low');
} else {
console.log('high');
}
return request(`/url/${a}`)
.then((data) => writeToFile(`${a}.txt`, data))
.catch((error) => console.log(error));
}
function testCase() {
//....
}
(a story)
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
for (var num of countToThree()) {
console.log(num);
}
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
const iterator = countToThree();
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
const iterator = countToThree();
iterator.next(); // { value: 1, done: false }
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
const iterator = countToThree();
iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
const iterator = countToThree();
iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
iterator.next(); // { value: 3, done: false }
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
const iterator = countToThree();
iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
iterator.next(); // { value: 3, done: false }
iterator.next(); // { value: 'finished', done: true }
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
for (var num of countToThree()) {
console.log(num);
}
// 1
// 2
// 3
function* countToThree() {
yield 1;
yield 2;
yield 3;
return 'finished';
}
const iterator = countToThree();
iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
iterator.next(); // { value: 3, done: false }
iterator.next(); // { value: 'finished', done: true }
iterator.next(); // { value: undefined, done: true }
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
// { value: 2, done: false }
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
// { value: 2, done: false }
iter.next(10);
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
// { value: 2, done: false }
iter.next(10);
// -> logs out -> second number: 10
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
// { value: 2, done: false }
iter.next(10);
// -> logs out -> second number: 10
// jump to next yield
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
// { value: 2, done: false }
iter.next(10);
// -> logs out -> second number: 10
// jump to next yield
// { value: 3, done: false }
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
// { value: 2, done: false }
iter.next(10);
// -> logs out -> second number: 10
// jump to next yield
// { value: 3, done: false }
iter.next(15);
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
// { value: 2, done: false }
iter.next(10);
// -> logs out -> second number: 10
// jump to next yield
// { value: 3, done: false }
iter.next(15);
// -> logs out -> third number: 15
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
// { value: 2, done: false }
iter.next(10);
// -> logs out -> second number: 10
// jump to next yield
// { value: 3, done: false }
iter.next(15);
// -> logs out -> third number: 15
// jumps to return statement
function* countToThreeWhileLogging() {
console.log('first number:', yield 1);
console.log('second number:', yield 2);
console.log('third number:', yield 3);
return 'finished';
}
const iter = countToThreeWhileLogging();
iter.next();
// generator jumps to first yield
// { value: 1, done: false }
iter.next(5);
// -> logs out -> first number: 5
// jumps to next yield
// { value: 2, done: false }
iter.next(10);
// -> logs out -> second number: 10
// jump to next yield
// { value: 3, done: false }
iter.next(15);
// -> logs out -> third number: 15
// jumps to return statement
// { value: 'finished', done: true }
function* countToThreeWhileLogging() {
console.log('first number:', 5);
console.log('second number:', 10);
console.log('third number:', 15);
return 'finished';
}
/**
* Creates descriptions
*/
function call(func, ...args) {
return {
type: '@@call',
func,
args
};
}
/**
* Example of creating an
* effect description
*/
const description = call(console.log, 'hello', 'world');
{
type: '@@call',
func: console.log,
args: ['hello', 'world']
}
/**
* Performing descriptions of side effects
*/
function performEffect({ type, func, args }) {
if (type === '@@call') {
return Promise.resolve(func(...args));
}
}
/**
* Create an effect description as a value
*/
/**
* Create an effect description as a value
*/
const description = call(console.log, 'hello', 'world');
/**
* Create an effect description as a value
*/
const description = call(console.log, 'hello', 'world');
/**
* Perform the effect described
*/
/**
* Create an effect description as a value
*/
const description = call(console.log, 'hello', 'world');
/**
* Perform the effect described
*/
performEffect(description);
/**
* Create an effect description as a value
*/
const description = call(console.log, 'hello', 'world');
/**
* Perform the effect described
*/
performEffect(description);
// -> hello world
performEffect({
type: '@@call',
func: console.log,
args: ['hello', 'world']
});
performEffect({
type: '@@call',
func: console.log,
args: ['hello', 'world']
});
// -> hello world
function runtime(generator) {
const iterator = generator();
const initialState = iterator.next();
function recursivelyResolve(state) {
return performEffect(state.value)
.then((result) => iterator.next(result))
.then((nextState) => {
return nextState.done ?
nextState.value :
recursivelyResolve(nextState)
});
}
return recursivelyResolve(initialState);
}
function procedure() {
const a = Math.random();
console.log(a);
if (a < 0.5) {
console.log('low');
} else {
console.log('high');
}
return request(`/url/${a}`)
.then((data) => writeToFile(`${a}.txt`, data))
.catch((error) => console.log(error));
}
function* procedure() {
const a = yield call(Math.random);
yield call(console.log, a);
if (a < 0.5) {
yield call(console.log, 'low');
} else {
yield call(console.log, 'high');
}
try {
const data = yield call(request, `/url/${a}`);
yield call(writeToFile, `${a}.txt`, data);
} catch (error) {
yield call(console.log, error);
}
}
procedure();
runtime(procedure);
function testCase() {
function testCase() {
const iterator = procedure();
function testCase() {
const iterator = procedure();
const step0 = iterator.next();
function testCase() {
const iterator = procedure();
const step0 = iterator.next();
expect(step0.value).toEqual({
type: '@@call',
func: Math.random,
args: []
});
function testCase() {
const iterator = procedure();
const step0 = iterator.next();
expect(step0.value).toEqual({
type: '@@call',
func: Math.random,
args: []
});
const step1 = iterator.next(0.2);
function testCase() {
const iterator = procedure();
const step0 = iterator.next();
expect(step0.value).toEqual({
type: '@@call',
func: Math.random,
args: []
});
const step1 = iterator.next(0.2);
expect(step1.value).toEqual({
type: '@@call',
func: console.log,
args: [0.2]
});
function testCase() {
//...continues
function testCase() {
//...continues
const step2 = iterator.next();
function testCase() {
//...continues
const step2 = iterator.next();
expect(step2.value).toEqual({
type: '@@call',
func: console.log,
args: ['low']
});
function testCase() {
//...continues
const step2 = iterator.next();
expect(step2.value).toEqual({
type: '@@call',
func: console.log,
args: ['low']
});
const step3 = iterator.next();
function testCase() {
//...continues
const step2 = iterator.next();
expect(step2.value).toEqual({
type: '@@call',
func: console.log,
args: ['low']
});
const step3 = iterator.next();
expect(step3.value).toEqual({
type: '@@call',
func: request,
args: [`/url/${0.2}`]
});
function testCase() {
//...continues
const step2 = iterator.next();
expect(step2.value).toEqual({
type: '@@call',
func: console.log,
args: ['low']
});
const step3 = iterator.next();
expect(step3.value).toEqual({
type: '@@call',
func: request,
args: [`/url/${0.2}`]
});
//...
function* slowPrint() {
while (true) {
const data = yield takeStream(process.stdin);
const chars = data.toString().split('');
let currentChar;
while (currentChar = chars.shift()) {
yield putStream(process.stdout, char);
yield delay(50);
}
}
}
runtime(slowPrint);
{
type: '@@takeStream',
stream: process.stdin,
args: []
}
{
type: '@@takeStream',
stream: process.stdin,
args: []
}
{
type: '@@putStream',
stream: process.stdin,
args: ['hello world']
}
{
type: '@@takeStream',
stream: process.stdin,
args: []
}
{
type: '@@putStream',
stream: process.stdin,
args: ['hello world']
}
{
type: '@@delay',
time: 50
}
By Eirik Langholm Vullum
A talk about side effects and JavaScript