Let's start with a more familiar example: using S.reduce
on an array.
> S.reduce (acc => s => acc + s) ('initial:') (['foo', 'bar', 'baz'])
'initial:foobarbaz'
Now, let's specialize the type of S.reduce
to explain the behaviour above.
S.reduce :: Foldable f => (b -> a -> b) -> b -> f a -> b
-- replace ‘Foldable f => f’ with ‘Array’ --
S.reduce :: (b -> a -> b) -> b -> Array a -> b
Next, let's specialize the type of S.reduce
to see how it will operate on Either values.
S.reduce :: Foldable f => (b -> a -> b) -> b -> f a -> b
-- replace ‘Foldable f => f’ with ‘Either x’ --
S.reduce :: (b -> a -> b) -> b -> Either x a -> b
What can we do when given S.Left ('foo')
as the Either x a
? We have an x
('foo'
) but no a
. Because we don't have an a
we cannot make use of the b -> a -> b
function. Thus, the only b
we can possibly return is the initial value.
> S.reduce (acc => s => acc + s) ('initial:') (S.Left ('foo'))
'initial:'
What can we do when given S.Right ('bar')
as the Either x a
? We have an a
, which we could feed to the b -> a -> b
function along with the initial value to produce another value of type b
.
> S.reduce (acc => s => acc + s) ('initial:') (S.Right ('bar'))
'initial:bar'
If S.reduce
were to return 'initial:'
or 'initial:bar:bar:bar'
in the case above it would still be conforming to the type signature, but the fantasy-land/reduce
implementation for Sanctuary's Either type applies the function exactly once when given a Right.