OI is a helper for passing multiple values through a promise/event chain.
.oi (i, o) ->
...
.oi ({a1, a2}, o) -> # 1. destructure what you need from i
o.a3 = ... # 2. assign new promises to o.[name]
.oi ({a1, a3}, o) -> # 3. use resolved new values and old ones
...
i
is the input from the previous step which we typically use with destructuring assignment.o
is an empty object to which we can assign new promises/values.- the return can be a promise, however the value is ignored.
- the result of the step is
mixin i, o
. values in o can overwrite same names in i.
When I discovered promises I often found myself saving values in scoped variables.
doSomething = ->
something = null # NO!
another = null # NOOOO!
Q().then ->
someFileOperation()
.then (_something) ->
something = _something # YUCK!
someDbThing(something.blah)
.then (_another) ->
another = _another # MORE YUCK!
.then ->
doStuff(something, another)
.then ->
close(something)
.then ->
move(another);
Those scope variables really bothers me. And are outright wrong for XQ style event chains where they would overwrite each other.
With XQ I made X.all()
work for objects as well as arrays, this
opened up for a new style.
Coffeescript destructuring to the rescue!
doSomething = ->
X().then ->
someFileOperation()
.then (something) ->
another = someDbThing(something.blah)
{something, another}
.all ({something, another}) -> # Sweet!
doStuff(something, another).then ->
{something, another}
.then ({something, another}) ->
...
However it's still a bit much to write when every step must explicitly end with what args are passed to the next, which in turn starts by just deconstructing the same args.
{something, another}
.all ({something, another}) ->
So one morning in yoga practice (ok, bad focus, I know), it came to me.
OI [choir music]
doSomething = ->
X().oi (o) ->
o.something = someFileOperation()
.oi ({something}, o) ->
o.another = someDbThing(something.blah)
.oi ({something, another}, o) ->
doStuff(something, another)
.oi ({something}, o) ->
close(something)
.oi ({another}, o) ->
move(another)
One can think of .oi
as a curried function with the following signature
oi(f)(i)
:: ((i, o) -> *) -> i -> o
An implemetation using fnuc is:
oi = curry (i, f) -> # fnuc does reverse curry
o = {}
r = if f.length == 1 then f(o) else f(i, o)
X(r).then -> X.all(mixin i, o)
This could be used with X.all()
.all oi (o, {a1}) ->
However I made an implementation straight in XQ.