I came across this interesting looking library:
https://stken2050.github.io/unlimitedjs/
It appears to be a simpler alternative to React and hooks - and the author makes it look very appealing in terms of being very simple and generic. It looks like this concept composes well, using plain JS that doesn't require any magic or a linter.
The documentation uses a lot of science jargon though - and the links to articles that explain the concepts are even worse, and the library code has been pretty heavily code-golfed.
I'm not a computer scientist, so...
What's special about a timeline monad?
How does it differ from a plain old pub/sub event-bus?
What's the advantage over pub/sub in the context of UI?
Does this somehow help with "unpredictable" events, like, say, chat messages?
Explain like I'm five please? 🙂
Top comments (3)
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.I can't really answer your questions, but I took a quick look at timeline-monad ( stken2050.github.io/timeline-monad/ ).
The idea seems to be very similar to js promises; except js promises can only be resolved or rejected once, whereas this code supports a sequence of events flowing from a single timeline. It would be like having a promise where the
then
function is called potentially multiple times. That seems to be its chief purpose as compared with promises.I'm not sure what happens when there are multiple processing steps and each processing step is asynchronous though. As new events keep triggering the
sync
functions, do they queue up in the subsequent steps to make sure order is preserved? My gut feeling is that this approach is problematic.Timeline monad uses [functional] reactive programming.
Reactive programming is simply focusing on everything as a stream.
It's kinda like eating a 3-course meal - you have a stream (over time) of meals being given to you which you consume one-at-a-time.
Or, you might want to process your 3-course meal by combining all meals (waiting till they are all given to you) and then consuming them all-at-once.
Streams are basically an array of values that will be filled up as time moves forward. You will have some "observer" that is watching the stream. As values get "pushed" into that array (or stream), you will have some series of functions chained together that will transform and modify each value(s) however needed.
So, for example, you can batch these values easily, combine them, etc.
This is the default in AngularJs apps when trying to build views that reactively render based on changing data.
What's different from pub-sub? Nothing in my mind... you are subscribing to an observable which emits events whenever a new value is available. Your subscribed handler is invoked at that time.