function equivalent of "do" notation
See original GitHub issueLet’s consider a problem which a hypothetical do
function might help us to solve:
Given an object, return the square root of the number represented by the string at path
a.b.c
. Do not assume, however, that the patha.b.c
will exist.
Here’s the obvious naive solution:
// naive :: Object -> Number
const naive = o => Math.sqrt(parseFloat(o.a.b.c));
This won’t handle all the following inputs:
{a: {b: {c: '2.25'}}}
{a: {b: {c: 'blah'}}}
{a: {b: {}}}
{a: {}}
{}
This is how I would solve this problem today:
// safe1 :: Object -> Maybe Number
const safe1 = R.pipe(
S.get('a'),
R.chain(S.get('b')),
R.chain(S.get('c')),
R.chain(S.parseFloat),
R.map(Math.sqrt)
);
With two small changes we can make this more uniform:
// safe2 :: Object -> Maybe Number
const safe2 = R.pipe(
S.Just,
R.chain(S.get('a')),
R.chain(S.get('b')),
R.chain(S.get('c')),
R.chain(S.parseFloat),
R.chain(R.compose(S.Just, Math.sqrt))
);
If we then changed the type from Object -> Maybe Number
to Maybe Object -> Maybe Number
we could remove the first step of the pipeline:
// safe3 :: Maybe Object -> Maybe Number
const safe3 = R.pipe(
R.chain(S.get('a')),
R.chain(S.get('b')),
R.chain(S.get('c')),
R.chain(S.parseFloat),
R.chain(R.compose(S.Just, Math.sqrt))
);
Now we have a form which can surely be abstracted! Given a list of functions we want to apply R.chain
to each, then apply R.pipe
to the list as positional arguments:
// $do :: Monad m => [(a -> m b), (b -> m c), ..., (y -> m z)] -> m a -> m z
const $do = fs => R.apply(R.pipe, R.map(R.chain, fs));
This can be written point-free:
// $do :: Monad m => [(a -> m b), (b -> m c), ..., (y -> m z)] -> m a -> m z
const $do = R.pipe(R.map(R.chain), R.apply(R.pipe));
Let’s rewrite safe3
in terms of $do
:
// safe4 :: Maybe Object -> Maybe Number
const safe4 = $do([
S.get('a'),
S.get('b'),
S.get('c'),
S.parseFloat,
R.compose(S.Just, Math.sqrt),
]);
$do
works for any Monad, and should reduce demand for “composed-chain” functions such as S.gets
.
What do you think? Shall we add this? If so, what should it be named? S.do
is problematic since do
is not a valid identifier. S.$do
is pretty ugly. Does do
go by other names?
Issue Analytics
- State:
- Created 8 years ago
- Comments:30 (23 by maintainers)
Top GitHub Comments
I recall you and @scott-christopher discussing that in the gitter room. i may take a whack at unifying our many composers into one.
Which raises the thought: A good name for a javascript composition lib would be Bach.js
Oh, duh. 🐐
The only limitation I see here is that this form of
do
notation only really allows you to feed through a single monadic value. You lose the ability to bind different variables to different monadic values in the same do expression like the following example: