When I read about the concept of lift, it's implemented like this (in Javascript)
const liftA2 = f => (a, b) => b.ap(a.map(f));
I realise there is a case in which liftA2
will produce an error: when b
is a Right
/Just
and a
is a Left
/Nothing
, because mapping a Left
/Nothing
will do nothing to the value when we would need it to become a partially applied function.
When a
and b
are both a Left
it doesn't blow up but of course the value of the returned Left
is going to be the value of b
which I suppose could be a problem depending on what you expect.
Is it a thing to lift a function to be used with these types? Should I systematically guard against these cases explicitly before using such a function? Is the above implementation correct/complete?
You will find more details about the issue bellow
Let us define the function to liftconst add = a => b => a + b;
In the case of a basic
Wrapper
implementingof
,ap
andmap
, we can follow what is happeningclass Wrapper { constructor(value) { this.value = value; } static of(value) { return new Wrapper(value); } map(f) { return Wrapper.of(f(this.value)); } ap(a) { return this.map(a.value); } } const a = Wrapper.of(1); const b = Wrapper.of(2); // liftA2 const tmp = a.map(add); // Wrapper { λ } b.ap(tmp); // Wrapper { 3 }
But the thing with
Either
orMaybe
is that they have aLeft
/Nothing
case wheremap
andap
are intended to do nothing specialclass Left { constructor(value) { this.value = value; } static of(value) { return new Left(value); } map(f) { return this; } ap(a) { return this; } } class Right{ constructor(value) { this.value = value; } static of(value) { return new Right(value); } map(f) { return Right.of(f(this.value)); } ap(a) { return this.map(a.value); } } const a = Left.of(1); const b = Right.of(2); // liftA2 const tmp = a.map(add); // Left { 1 } b.ap(tmp); // Error because tmp's value is not a function