I try to translate traverse
/sequenceA
to Javascript. Now the following behavior of the Haskell implementation gives me trouble:
traverse (\x -> x) Nothing -- yields Nothing
sequenceA Nothing -- yields Nothing
traverse (\x -> x) (Just [7]) -- yields [Just 7]
sequenceA (Just [7]) -- yields [Just 7]
As a Haskell newbie I wonder why the first expression works at all:
instance Traversable Maybe where
traverse _ Nothing = pure Nothing
traverse f (Just x) = Just <$> f x
pure Nothing
shouldn't work in this case, since there is no minimal applicative context in which the value could be put in. It seems as if the compiler checks the type of this expression lazily and since mapping the id function over Nothing
is a noop, it simply "overlooks" the type error, so to speak.
Here is my Javascript translation:
(Please note, that since Javascirpt's prototype system is insufficient for a couple of type classes and there is no strict type checking anyway, I work with Church encoding and pass type constraints to functions explicitly.)
// minimal type system realized with `Symbol`s
const $tag = Symbol.for("ftor/tag");
const $Option = Symbol.for("ftor/Option");
const Option = {};
// value constructors (Church encoded)
const Some = x => {
const Some = r => {
const Some = f => f(x);
return Some[$tag] = "Some", Some[$Option] = true, Some;
};
return Some[$tag] = "Some", Some[$Option] = true, Some;
};
const None = r => {
const None = f => r;
return None[$tag] = "None", None[$Option] = true, None;
};
None[$tag] = "None";
None[$Option] = true;
// Traversable
// of/map are explicit arguments of traverse to imitate type inference
// tx[$Option] is just duck typing to enforce the Option type
// of == pure in Javascript
Option.traverse = (of, map) => ft => tx =>
tx[$Option] && tx(of(None)) (x => map(Some) (ft(x)));
// (partial) Array instance of Applicative
const map = f => xs => xs.map(f);
const of = x => [x];
// helpers
const I = x => x;
// applying
Option.traverse(of, map) (I) (None) // ~ [None]
Option.traverse(of, map) (I) (Some([7])) // ~ [Some(7)]
Obviously, this translation deviates from the Haskell implementation, because I get a [None]
where I should get a None
. Honestly, this behavior corresponds precisely to my intuition, but I guess intuition isn't that helpful in functional programming. Now my question is
GHCi does not overlook any type error. It defaults an unconstrained Applicative
to IO
, but you only get this behavior in a GHCi prompt (and not a .hs
source file). You can check
> :t pure Nothing
pure Nothing :: Applicative f => f (Maybe b)
But still have
> pure Nothing
Nothing
Your javascript implementation is fine; you passed in an Applicative
instance for arrays and got what is expected.