1

I've tried to understand Haskell's traverse function and to implement it in Javascript, but I'm stuck. When I look at its type (Functor t, Foldable t, Applicative f) => (a -> f b) -> t a -> f (t b), I understandd that I need <$> (map in JS), <*> (ap in JS) and fold in order to implement it.

I came up with the following code:

// Church encoded Identity newtype

const Ident = x => f => f(x);

const traverse = map => ft => tx => map(y => Ident(y)) (tx(x => ft(x)));

// list functor

map = f => xs => xs.map(f);

// helper

I = x => x;

// data

idA = Ident(1);
idB = Ident(null);

// and run

const r1 = traverse(map) (x => x === null ? [] : [x]) (idA); // [Ident(1)]
const r2 = traverse(map) (x => x === null ? [] : [x]) (idB); // []

console.log(r1 [0] (I)); // 1
console.log(r2); // []

Since I used Ident and Array the type simplifies to (a -> [b]) -> Ident a -> [Ident b]. traverse runs the effect when it rebuilds the data structure. If I am not mistaken one can observe the non-deterministic effect of the list functor in my example.

But where is the applicative? And why does the traversable have to be a functor? Is it just a coincidence that I didn't need them for my sketch?

  • 1
    I think you mixed up the two types. (Though I guess both `Ident` and `Array` are Traversables as well as Applicative Functors), usually `Array` is the traversed one and `Ident` is the Applicative. So `(a -> Ident b) -> [a] -> Ident [b]` is more common – Bergi May 23 '17 at 20:29
  • Since `Ident(x)` is `f => f(x)`, I'd say this is the `Cont r` continuation functor (and monad) rather than the identity functor (and monad). Am I wrong on this? – chi May 23 '17 at 20:33
  • 1
    `tx(x => ft(x))` is much too specific, only for `Ident`. Try implementing it for generic traversables and applicatives – Bergi May 23 '17 at 20:33
  • 1
    It looks to me (my JS is very flaky) to be the right sort of thing. But it's a very peculiar special case. You can implement traversability of `Ident` (which packs up exactly one value) using only functoriality of "lists". If you traverse a structure which can store zero or more than one value, you will need the full applicative apparatus. – pigworker May 23 '17 at 20:35
  • @pigworker, Bergi: So I guess I am not completely on the wrong track, but my example is too specific to be meaningful. I'm going to implement it for other types and derive a more generic solution. Thank you! –  May 23 '17 at 20:52
  • You can also try to use an `Ident` type that is not (directly) church-encoded, where unwrapping is too easy to confuse with standard function application. Make `Ident` produce actual objects with `ap`, `of` and `map` methods, and then use those in your implementation of `traverse` instead of peeking into the internal representation. – Bergi May 23 '17 at 21:17
  • @Bergi I guess you mean `tx(x => ft(x))`. I'll think about it. –  May 23 '17 at 21:45
  • @chi Yes, it is continuation passing. I tried to implement `newtype IdentityFn x = IdFn { runIdFn :: forall z. (x -> z) -> z }`. The trick here seems to be the explicit `forall z`, which introduces a fresh type variable that doesn't appear in the body of the expression. That is the part I don't fully understand. But I guess I don't have to, because Javascript doesn't have any type checking. –  May 24 '17 at 08:39
  • @Bergi Ha, I've just read "_Church encoding is just a hack for those, who don't have adequate mechanism for defining data types, because primitive recursion is too weak._". Maybe you're right and I should just dump my approach. –  May 24 '17 at 09:42
  • @ftor I see. Indeed `forall z. (x -> z) -> z` is isomorphic to `x` (by Yoneda). I still wonder why the CPS style was needed, but it is a nice exercise anyway. – chi May 24 '17 at 11:25
  • @chi There is a trend in functional JS to use the `Identity` functor to allow pure functions to be chained like methods (`Ident(5).map(sqr).map(inc)...`. So you end up mapping the whole world. There are several other issues with the Javascript's limited prototype system and `this`, when it comes to functional programming. Hence, I try to avoid it altogether. Church/Scott encoding seems to be a promising approach for an untyped language. –  May 24 '17 at 12:09
  • @ftor Ah, that makes sense, since it explains why `Ident = x => x` would fail. I'd probably be tempted by more OOP approaches like `Ident = x => { unIdent : x }`, or whatever JS requires to attach methods. Maybe a constructor function? Anyway, thanks for the clarification. – chi May 24 '17 at 12:42

0 Answers0