Skip to content

Instantly share code, notes, and snippets.

@DrBoolean
Last active May 24, 2021 02:31
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DrBoolean/917f9a34fd87716a455493c96a868633 to your computer and use it in GitHub Desktop.
Save DrBoolean/917f9a34fd87716a455493c96a868633 to your computer and use it in GitHub Desktop.
Tail of three envs
const compose = (f, g) => x => f(g(x))
const Id = x =>
({
fold: f => f(x),
map: f => Id(f(x))
})
Id.of = Id
const Tuple = (_1, _2) =>
({
_1,
_2,
map: f => Tuple(_1, f(_2)),
extend: f => Tuple(_1, f(Tuple(_1, _2))),
extract: () => _2
})
Tuple.of = x => Tuple(x, x)
const Fn = run =>
({
run,
map: f => Fn(x => f(run(x))),
chain: f => Fn(x => f(run(x)).run(x))
})
Fn.of = x => Fn(() => x)
const objToString = style =>
Object.keys(style)
.reduce((acc, key) =>
acc.concat(`${key}: ${style[key]}`), '') // this is foldMap
const Html = {
div: content =>
({tag: 'div', content, style:{}}),
setStyle: (el, style) =>
Object.assign({}, el, {style}),
render: (el, style) =>
`<${el.tag} style=${objToString(el.style)}>${el.content}</${el.tag}>`,
}
// EXAMPLE 1
//==============
// Toss it in a closure.
// Benefits:
// Easy to implement
// Drawbacks:
// Everything has access to settings everywhere whether it needs it or not.
// Implicitly stateful
// Need data up front
const ClosureExample = settings => {
const createPage = content =>
Id.of(content)
.map(Html.div)
.map(div =>
Html.setStyle(div, {'background-color': settings.favoriteColor})
)
.fold(Html.render)
const getContent = id =>
({
1: 'hi i am some content',
2: 'i am more content'
})[id]
const blog = compose(createPage, getContent)
return blog
}
// EXAMPLE 2
//==============
// Use Fn monad (called Reader)
// Benefits:
// Things which need access to the environment (Fn) are explicit
// Things which don't need access, don't have access
// Lazy access so you can pass the stuff in later.
// Implements standard monadic interface
// Drawbacks:
// Bit tricky to understand at first
const ReaderExample = () => {
const createPage = content =>
Fn.of(content)
.map(Html.div)
.chain(div =>
Fn(settings =>
Html.setStyle(div, {'background-color': settings.favoriteColor})
)
)
.map(Html.render)
const getContent = id =>
({
1: 'hi i am some content',
2: 'i am more content'
})[id]
const blog = id =>
Fn.of(id)
.map(getContent)
.chain(createPage)
return blog
}
// EXAMPLE 3
//==============
// Use Tuple comonad (called env)
// Benefits:
// Things which need access to the environment (Fn) are explicit
// Things which don't need access, don't have access
// Implements standard comonadic interface
// Drawbacks:
// Tricky to understand at first
// Need data up front
const TupleExample = () => {
const createPage = tuple =>
tuple
.map(Html.div)
.extend(({_1: settings, _2: div}) =>
Html.setStyle(div, {'background-color': settings.favoriteColor})
)
.map(Html.render)
.extract()
const getContent = id =>
({
1: 'hi i am some content',
2: 'i am more content'
})[id]
const blog = tuple =>
tuple.map(getContent)
.extend(createPage)
.extract()
return blog
}
const settings = {favoriteColor: 'green'}
const closureExample = ClosureExample(settings)
const readerExample = ReaderExample()
const tupleExample = TupleExample()
console.log('closure', closureExample(1))
console.log('reader', readerExample(1).run(settings))
console.log('tuple', tupleExample(Tuple(settings, 1)))
//closure <div style=background-color: green>hi i am some content</div>
//reader <div style=background-color: green>hi i am some content</div>
//tuple <div style=background-color: green>hi i am some content</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment