LUH3417 LUH3417 - 5 months ago 18
Javascript Question

How to implement a functor so that map can be applied to two functions?

In Haskell you can apply

fmap
to two functions, which is basically function composition. You can even compose
fmap
to enable function composition of functions with higher arity (
fmap . fmap
).

This works because functions are functors.

How would such a functor (or the appropriate
map
method) be implemented in Javascript?

This is what I have tried so far:

funcProto = {
map(f) { return y => f(this.x(y)) }
};

function func(x) {
return Object.assign(Object.create(funcProto), {x: x});
}

const comp = f => g => x => f(g(x));
const map = f => ftor => ftor.map(f);
const sub = y => x => x - y;
const sqr = x => x * x;
const inc = x => x + 1;


This works for normal function composition:

func(sqr).map(inc)(2); // 5


However, it doesn't work for a composed version of
map
:

const map2 = comp(map)(map);
map2(sub)(sub)(10)(5)(4); // Error


I think I adapt myself too much to the traditional way functors are implemented in Javascript. Functions as functors behave differently from list or maybe.

Answer

In Haskell, everything is a function. In your javascript, some of your functions are represented as funcs with an .x() method, and some are native Functions. That cannot work.

Here are three approaches:

const sub = y => x => x - y;
const sqr = x => x * x;
const inc = x => x + 1;
const comp = f => g => x => f(g(x));
  • plain functions, no methods.

    const fmap = comp; // for functions only
    console.log(fmap(inc)(sqr)(1)) // 5
    const fmap2 = comp(fmap)(fmap);
    console.log(fmap2(sub)(sub)(10)(5)(4)); // 9
    
  • extending native Functions, using fmap as a method:

    Function.prototype.fmap = function(f) { return comp(this)(f); };
    console.log(sqr.fmap(inc)(1)); // 5
    const fmap2 = comp.fmap(comp) // not exactly what you want, works just like above
    Function.prototype.fmap2 = function(f) { return this.fmap(g => g.fmap(f)); } // better
    console.log(sub.fmap2(sub)(10)(5)(4)); // 9
    
  • building your own function type (also in ES6):

    function Func(f) {
        if (!new.target) return new Func(f);
        this.call = f;
    }
    // Ahem.
    const sub = Func(y => Func(x => x - y));
    const sqr = Func(x => x * x);
    const inc = Func(x => x + 1);
    const comp = Func(f => Func(g => Func(x => f.call(g.call(x)))));
    // Now let's start
    const fmap = Func(f => Func(x => x.fmap(f))); // a typeclass!
    Func.prototype.fmap = function(f) { return comp(this)(f); }; // an instance of the class!
    console.log(fmap.call(inc).call(sqr).call(1)); // 5
    const fmap2 = comp.call(fmap).call(fmap);
    console.log(fmap2.call(sub).call(sub).call(10).call(5).call(4)); // 9