Created
February 3, 2017 05:34
-
-
Save CreaturePhil/41ff7a15cda2533a2d42f1e6344cbe7c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {Component} from 'react'; | |
import ReactDOM from 'react-dom'; | |
import './index.css'; | |
const Comp = g => | |
({ | |
fold: g, | |
contramap: f => | |
Comp(x => g(f(x))), | |
concat: other => | |
Comp(x => <div>{g(x)} {other.fold(x)}</div>) | |
}) | |
// Object -> JSX | |
const ProfileLink = user => | |
<a href={`/users/${user.id}`}>{user.name}</a> | |
// String -> JSX | |
const Heading = str => | |
<h1>Now Viewing {str}</h1> | |
// Comp | |
const Title = Comp(Heading).contramap(s => s.pageName) | |
// f = s => s.pageName :: Object -> String | |
// g = Heading :: String -> JSX | |
// g' = x => g(f(x)) :: Object -> Comp | |
// intermediate process: g . f :: Object -> String -> JSX | |
// g . f :: Object -> JSX | |
const Link = Comp(ProfileLink).contramap(s => s.currentUser) | |
// const App = Title | |
// .concat(Link) | |
// .concat(Link) | |
// .concat(Title) | |
// referential transparency | |
// Title.fold({ pageName: 'Home '}) | |
// g({pageName: 'Home'}) | |
// ({pageName: 'Home'}) => g(f(x)) | |
// Heading((s => s.pageName)({pageName: 'Home'})) | |
// Heading('Home') | |
// <h1> Now Viewing {str}</h1> | |
// So basically, it calls Comp(g).contramap(f) | |
// f is called first then and it passes it into g | |
// compose(g, f) goes from f to g | |
// Comp(compose(g, f)) | |
// concat adds another element as a non parent-child | |
// not parent/cild of each other. just next to each other | |
// can think of concat as a list | |
// Reducer :: (a, b) -> a | |
// contramap runs on each element beforehand | |
// map runs on the accumulator afterwards | |
// concat is basically combine reducers | |
const Reducer = g => | |
({ | |
fold: g, | |
contramap: f => | |
Reducer((acc, x) => g(acc, f(x))), | |
map: f => | |
Reducer((acc, x) => f(g(acc, x))), | |
concat: o => | |
Reducer((acc, x) => o.fold(g(acc, x), x)) | |
}) | |
const r = Reducer((acc, x) => acc.concat(x)) | |
.contramap(x => `The number is ${x}`) | |
.map(x => x + '! ') | |
// Reducer((acc, x) => acc.concat(x)) | |
// g = (acc, x) => acc.concat(x) | |
// contramap calls f(x) and puts it pack as the 2nd argument to g | |
// contramap: | |
// f = x => `The number is ${x}` | |
// return: Reducer((acc, x) => g(acc, f(x))) | |
// g(acc, f(x)) = ((acc, x) => acc.concat(x))(acc, (x => `The number is ${x}`)(x)) | |
// new g = (acc, x) => ((acc, x) => acc.concat(x))(acc, (x => `The number is ${x}`)(x)) | |
// map calls g and pass it to f | |
// map: | |
// f = x => x + '!' | |
// return: Reducer((acc, x) => f(g(acc, x))) | |
// g(acc, x) = ((acc, x) => ((acc, x) => acc.concat(x))(acc, (x => `The number is ${x}`)(x)))(acc, x) | |
// f = (x => x + '!')((acc, x) => ((acc, x) => acc.concat(x))(acc, (x => `The number is ${x}`)(x)))(acc, x) | |
// new g is (acc, x) => f(g(acc, x)) or | |
// (acc, x) => (x => x + '!')((acc, x) => ((acc, x) => acc.concat(x))(acc, (x => `The number is ${x}`)(x)))(acc, x) | |
console.log([1,2,3].reduce(r.fold, '')) | |
// calling fold | |
// g = (acc, x) => (x => x + '!')((acc, x) => ((acc, x) => acc.concat(x))(acc, (x => `The number is ${x}`)(x)))(acc, x) | |
// g('', 1) where acc = '' and x = 1 | |
// contramap: The number is 1 | |
// pass it back to the (acc, x) => acc.concat | |
// The number is 1 | |
// map: The number is 1! | |
// The number is 1! | |
// The number is 1! The number is 2! The number is 3! | |
const r1 = Reducer((acc, x) => acc.concat(x)) | |
.map(x => `The number is ${x}`) | |
.map(x => x + '! ') | |
console.log('r1', [1,2,3].reduce(r1.fold, '')) | |
// show diff | |
// for each input it is mapped to | |
const r2 = Reducer((acc, x) => acc.concat(x)) | |
.map(x => x + '! ') | |
.contramap(x => `The number is ${x}`) | |
console.log('r2', [1,2,3].reduce(r2.fold, '')) | |
const appReducer = Reducer((state, action) => { | |
switch (action.type) { | |
case 'set_visibility_filter': | |
return Object.assign({}, state, { | |
visibilityFilter: action.filter | |
}) | |
default: | |
return state | |
} | |
}) | |
const todoReducer = Reducer((state, action) => { | |
switch (action.type) { | |
case 'new_todo': | |
const t = { id: 0, title: action.payload.title } | |
return Object.assign({}, state, { | |
todos: state.todos.concat(t) | |
}) | |
default: | |
return state | |
} | |
}) | |
// Hoc :: Component -> Component | |
const Hoc = g => | |
({ | |
fold: g, | |
concat: other => | |
Hoc(x => g(other.fold(x))) | |
}) | |
// isomorphism | |
const classToFn = C => | |
(props) => <C {...props} /> | |
const withReducer = (stateName, dispatchName, reducer, initialState) => | |
BaseComponent => { | |
return class extends Component { | |
constructor(props) { | |
super(props); | |
this.state = {stateValue: initialState}; | |
this.dispatch = this.dispatch.bind(this); | |
} | |
dispatch(action) { | |
const state = ({ stateValue }) => ({stateValue: reducer(stateValue, action)}); | |
this.setState(state); | |
} | |
render() { | |
const props = Object.assign({}, this.props, { | |
[stateName]: this.state.stateValue, | |
[dispatchName]: this.dispatch | |
}); | |
return ( | |
<div> | |
<BaseComponent {...props} /> | |
</div> | |
); | |
} | |
} | |
} | |
const todoApp = appReducer.concat(todoReducer) | |
.contramap(action => Object.assign({filter: 'all'}, action)) | |
.map(s => Object.assign({}, s, {lastUpdated: Date()})) | |
const aState = {visibilityFilter: 'complete'} | |
const aAction = {type: 'set_visibility_filter'} | |
const res = todoApp.fold(aState, aAction) | |
console.log(res) | |
const hoc = Hoc(withReducer('state', 'dispatch', todoApp.fold, {todos: []})) | |
const Todos = hoc.fold(({ state, dispatch }) => | |
<div> | |
<span>Filter: {state.visibilityFilter}</span> | |
<ul> | |
{ state.todos.map((t, i) => <li key={i}>{t.title}</li>) } | |
</ul> | |
<button onClick={() => | |
dispatch({ type: 'new_todo', payload: {title: 'New todo'}})}> | |
Add Todo | |
</button> | |
<button onClick={() => | |
dispatch({ type: 'set_visibility_filter' })}> | |
Set Visibility | |
</button> | |
<div> | |
<pre> | |
<code>{JSON.stringify(state, null, 2)}</code> | |
</pre> | |
</div> | |
</div> | |
) | |
// classToFn is not really use, Comp here not really use either xaa in todo app | |
const TodoComp = Comp(classToFn(Todos)) | |
const App = Title.contramap(s => s.pageName) | |
.concat(TodoComp) | |
.concat(Link) | |
.fold({ pageName: 'Home', currentUser: {id: 2, name: 'Phil La'} }) | |
ReactDOM.render( | |
App, | |
document.getElementById('root') | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment