The special thing is that the timeline sync method can itself return new timelines, and the result is a single merged timeline. The timeline-monad library is not unique in this respect – RxJS has similar capabilities.
To explain more of what a monad is, we really need to cover functors. A functor is some structure whose values can be mapped, leaving the structure itself alone. Examples:
Arrays are functors: mapping str => str.length over an array of two elements['hi', 'sup'] yields another array of two elements[2, 3].
Promises are (almost*) functors: mapping str => str.length over a promise forhello yields another promise for5 (sadly, TC39 named this method .then instead of .map)
Trees can be functors: mapping str => str.length over a tree of triangular shapetree('hi', tree('left'), tree('right')) yields another tree of triangular shapetree(2, tree(4), tree(5)).
Timelines are also functors: timelineA.sync(str => str.length) yields another timeline, whose values are published at the same time as timelineA but which are numbers instead of strings. The "timeline" structure is identical, but the values are transformed.
Cool cool, so where's the monad?
Well, a monad is a functor which also allows you to "put a value in the monad" AND (this is the really special bit) lets you fuse nested layers of structure in a "sensible" (law-abiding) way. Examples:
Arrays are monads: you can put a single value into an array (v => [v]), and you can flatten nested layers of arrays ([[5], [1, 4], []].flat() yields [5, 1, 4])
Promises are (almost*) monads: you can put a single value into a promise (v => Promise.resolve(v)) and you can flatten nested layers of promise (promiseA.then(a => Promise.resolve(a + 1)) yields not a nested promise, but rather just a single-layer promise; put another way, Promise.resolve(Promise.resolve(1)) is equivalent to just Promise.resolve(1)).
Timeline values from this library are also monadic.
// a timeline of timelines? Nope, a *merged* timelineconstthreePingsPerClickT=mouseClicksT.sync(_=>createThreePingsT())
If you return a timeline from a timeline.sync callback, the returned timeline from sync is a single-layer aggregation of events "flattened" down into one timeline – the events will be those resulting from the "inner" returned timelines.
*Special note: to be truly functors or monads, structures must obey specific mathematical laws that guarantee their behavior is sensible and dependable. Those laws are out of scope for this post, but unfortunately Promises do not adhere to them, specifically because instead of having separate map and flatten methods, they have a single method then. That means that sometimes, then maps (like a functor) and sometimes, it flattens (like a monad) – meaning it doesn't strictly obey either sets of laws 100% of the time.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
The special thing is that the timeline
sync
method can itself return new timelines, and the result is a single merged timeline. Thetimeline-monad
library is not unique in this respect – RxJS has similar capabilities.To explain more of what a monad is, we really need to cover functors. A functor is some structure whose values can be mapped, leaving the structure itself alone. Examples:
str => str.length
over an array of two elements['hi', 'sup']
yields another array of two elements[2, 3]
.str => str.length
over a promise forhello
yields another promise for5
(sadly, TC39 named this method.then
instead of.map
)str => str.length
over a tree of triangular shapetree('hi', tree('left'), tree('right'))
yields another tree of triangular shapetree(2, tree(4), tree(5))
.Timelines are also functors:
timelineA.sync(str => str.length)
yields another timeline, whose values are published at the same time astimelineA
but which are numbers instead of strings. The "timeline" structure is identical, but the values are transformed.Cool cool, so where's the monad?
Well, a monad is a functor which also allows you to "put a value in the monad" AND (this is the really special bit) lets you fuse nested layers of structure in a "sensible" (law-abiding) way. Examples:
v => [v]
), and you can flatten nested layers of arrays ([[5], [1, 4], []].flat()
yields[5, 1, 4]
)v => Promise.resolve(v)
) and you can flatten nested layers of promise (promiseA.then(a => Promise.resolve(a + 1))
yields not a nested promise, but rather just a single-layer promise; put another way,Promise.resolve(Promise.resolve(1))
is equivalent to justPromise.resolve(1)
).Timeline values from this library are also monadic.
If you return a timeline from a
timeline.sync
callback, the returned timeline fromsync
is a single-layer aggregation of events "flattened" down into one timeline – the events will be those resulting from the "inner" returned timelines.*Special note: to be truly functors or monads, structures must obey specific mathematical laws that guarantee their behavior is sensible and dependable. Those laws are out of scope for this post, but unfortunately Promises do not adhere to them, specifically because instead of having separate
map
andflatten
methods, they have a single methodthen
. That means that sometimes,then
maps (like a functor) and sometimes, it flattens (like a monad) – meaning it doesn't strictly obey either sets of laws 100% of the time.