elm-types Strawman

import t from 'elm-types';

const AddTodo = t.Constructor('AddTodo', [t.String]);
const RemoveTodo = t.Constructor('AddTodo', [t.String]);
const Action = t.Union('Action', [AddTodo]);
const Todo = t.Record('Todo', {
    name: t.String,
    completed: t.Bool
});
const Model = t.Record('Model', {
    todos: t.List('todos', Todo)
});

// Throws if function is not passed correct parameter types
const update = t.Function('update', [State, Action, State], (state, action) =>
    // Throws if last object is not exaustive
    t.case(action, Action, {
        [AddTodo]: add => ({
            ...state,
            todos: [...state.todos, Todo(add, false)]
        }),
        [RemoveTodo]: remove => ({
            ...state,
            todos: state.todos.filter(todo => todo.name === remove)
        })
    });
);

const store = createStore(update, Model([]));

const App = ({ todos }) => (
    <div>
        <h1>Todo List</h1>
        <ul>
            {todos.map(todo => (
                <li onClick={() => store.dispatch(RemoveTodo(todo.name))}>
                    {todo.name}
                </li>
            ))}
        </ul>
    </div>
);

const AppProps = t.Record('AppProps', {
    todos: t.List('todos', Todo)
});

App.propTypes = t.PropTypes(AppProps);

render(
    <App {...store.getState()} />,
    document.getElementById('app')
);

flatten two ways

function flatten(source, acc = []) {
    if (source.length === 0) return acc;

    const [head, ...tail] = source;

    return flatten(tail, [...acc, ...(Array.isArray(head) ? flatten(head) : [head])]);
}

console.log(flatten([1,2,[3,4,5,[6,7],8,[9]],10]));
import * as R from 'ramda';

const flatten = R.ifElse(
    R.propSatisfies(R.equals(0), 'length'),
    R.flip(R.identity),
    R.converge((source, acc = []) => flatten(source, acc), [
	    R.tail,
        R.useWith(R.flip(R.concat), [
	        R.pipe(R.head, R.ifElse(Array.isArray, head => flatten(head, []), R.of)),
	        R.identity
        ])
    ])
);

console.log(flatten([1,2,[3,4,5,[6,7],8,[9]],10], []));

Arrow function problems

function foo(ddb) {
  return {
    listTables: (params = {}, cb = idFunc) => {
      const self = this
      let currentList = params.currentList || []
      
      return toPromise(ddb, ddb.listTables, omit(params, 'currentList'), cb)
        .then(r => {
          console.log('LISTTABLES result', r)
          currentList = currentList.concat(r.TableNames || [])
          
          if (!r.LastEvaluatedTableName || r.TableNames.length === 0) {
            return { ...r, TableNames: currentList }
          }

          return self.listTables({   // <- Fails here
              ...params,
              currentList,
              ExclusiveStartTableName: r.LastEvaluatedTableName,
            }, cb)
        })
    }
  }
}
class MyClass {
  myMethod = () => {
    // ...code
  }
}

Brookjs components strawman proposal

import { h, component } from 'brookjs'
import { editClick } from '../actions'

// Changes only occur when bound to a stream
export default component({
    render: props$ => (
        <li>
            {/* Text changes when a value is emitted. */}
            <p class="todo__name">
                {props$.map(props => props.text)}
            </p>
            {/* Maps a stream of events to actions. */}
            <button onClick={event$ => event$.map(editClick)}>
                {'Edit todo'}
            </button>
        </li>
    )
})
import { h, component } from 'brookjs'
import TodoItem from './TodoItem'

export default component({
    render: props$ => (
        <div>
            <h2>My Todos</h2>
            {/**
              * `order` provides sequence todos should appear
              * `dict` provides a performant instance lookup 
              */}
            <ul>
                {props$.map(todos => todos.order.map(key => (
                    <TodoItem
                        props$={props$.map(todos => todos.dict[key])}
                        key={key}
                        preplug={instance$ => instance$.map(action => ({
                            ...action,
                            meta: { key }
                        })} />
                )))}
            <ul>
        </div>
    )
})
import { h, component } from 'brookjs'
import TodoList from './TodoList'

export default component({
    render: props$ => (
        <div>
            <h1>Todo App</h1>
            {/**
              * Individual attributes respond to observables
              * Performance optimized inline
              */}
            <input value={props$.map(props => props.editing).skipDuplicates()}
                   onInput={event$ => event$.map(event => editTodo(event.target.value))} />
            <button onClick={event$ => event$.map(addTodo)}>Add Todo</button>
            <TodoList props$={props$.map(props => props.todos)} />
        </div>
    )
})
import { createStore, applyMiddleware } from 'redux'
import { h, observeDelta, Kefir } from brookjs
import { App } from './components'
import { selectProps } from './selectors'

const store = createStore(
    (state, action) => state, // reducer
    window.__INITIAL_STATE__ || {},
    applyMiddleware(observeDelta(
        /* register deltas here */
    )
)
const state$ = Kefir.fromESObservable(store)

/**
 * `mount` thus takes the DOM to mount
 * and the element to bind it to, and
 * returns a stream. Note that because
 * of how streams work, nothing happens
 * until the stream is observed.
 */
const view$ = mount(
    <App props$={selectProps(state$)} />,
    document.getElementById('app')
)
 
view$.observe(store.dispatch)
import { h, component, list } from 'brookjs'
import TodoItem from './TodoItem'

export default component({
    render: props$ => (
        <div>
            <h2>My Todos</h2>
            <ul>
                {/* Must be a stream of objects with `order` & `dict` */}
                {list(props$, (props$, key) => (
                    <TodoItem
                        props$={props$}
                        key={key}
                        preplug={instance$ => instance$.map(action => ({
                            ...action,
                            meta: { key }
                        })} />
                )}
            <ul>
        </div>
    )
})
import { createStore, applyMiddleware } from 'redux'
import { h, observeDelta, Kefir } from brookjs
import { App } from './components'
import { selectProps } from './selectors'

const el = document.getElementById('app')

const store = createStore(
    (state, action) => state,
    window.__INITIAL_STATE__ || {},
    applyMiddleware(observeDelta(
        /* register deltas here */
        domDelta({ el, selectProps, view: App })
    )
)

// Everything is bound to the store immediately,
// but an init action makes sure everything waits
// until the store is fully instantiated.
store.dispatch(init())

Point-Free Style – Functional Light JS v2 from Front-End Masters

function output(txt) {
	console.log(txt);
}

function printIf(predicate) {
	return function(msg) {
		if (predicate(msg)) {
			output(msg);
		}
	};
}

function isShortEnough(str) {
	return str.length <= 5;
}

function isLongEnough(str) {
	return !isShortEnough(str);
}

var msg1 = "Hello";
var msg2 = msg1 + " World";

printIf(isShortEnough)(msg1);		// Hello
printIf(isShortEnough)(msg2);
printIf(isLongEnough)(msg1);
printIf(isLongEnough)(msg2);		// Hello World
const output = console.log.bind(console);
const when = func => pred => val => {
  if (predicate(val)) {
    func(val);
  }
};
const not = func => val => !func(val);
const printIf = when(output);
const isShortEnough = str => str.length <= 5;
const isLongEnough = not(isShortEnough);

Simple Path

const getPath = (path, target) =>
    path.split('.')
        .reduce((value, key) => value[key], target);

// Usage
const path = 'a.b.c';
const target = {
    a: {
        b: {
            c: true
        }
    }
};

assert(true === getPath(path, target));

Removed brookjs Actions

/**
 * Value change Action type.
 *
 * @type {string}
 */
export const VALUE_CHANGE = 'VALUE_CHANGE';

/**
 * Create a new Value Change action.
 *
 * @param {string} value - Target value.
 * @returns {Action} Value Change action object.
 */
export function valueEventAction(value) {
    return {
        type: VALUE_CHANGE,
        payload: { value }
    };
}

/**
 * Field focus event action type.
 *
 * @type {string}
 */
export const FIELD_FOCUS = 'FIELD_FOCUS';

/**
 * Create a FIELD_FOCUS action object.
 *
 * @param {string} name - Field name.
 * @returns {Action} FIELD_FOCUS action.
 */
export function fieldFocusAction(name) {
    return {
        type: FIELD_FOCUS,
        payload: { name }
    };
}

/**
 * Checked change constant.
 *
 * @type {string}
 */
export const CHECKED_CHANGE = 'CHECKED_CHANGE';

/**
 * Create a new Checked Change action.
 *
 * @param {boolean} value - Target checked.
 * @returns {Action} Checked Change action object.
 */
export function checkedEventAction(value) {
    return {
        type: CHECKED_CHANGE,
        payload: { value }
    };
}

/**
 * Click event Action type.
 *
 * @type {string}
 */
export const CLICK = 'CLICK';

/**
 * Create a new Clicked Action
 *
 * @returns {Action} Clicked action object.
 */
export function clickedEventAction() {
    return { type: CLICK };
}

Observable Rendering

import Kefir from 'kefir';

export default function EditorComponent(el, props$) {
    const keyup$ = Kefir.fromEvents(el, 'keyup');
    const keydown$ = Kefir.fromEvents(el, 'keydown');

    return props$.sampledBy(keyup$.debounce(150))
        .flatMapLatest(props =>
            createRenderStream(props).takeUntil(keydown$)
        );

    function createRenderStream(props) {
        return Kefir.stream(emitter => {
            const loop = requestAnimationFrame(() => {
                // Update the element
                emitter.end();
            });

            return () => cancelAnimationFrame(loop);
        });
    }
};