1

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.

2 Answers2

1

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
    
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • `const fmap = comp;` is very humorous, question answered. Seriously, `fmap` and `(.)` have different types in Haskell, so I wondered why they are equivalent (provided fmap is applied to two functions). And you just say `fmap === comp`, deal with it. OK then, there is no practical difference between `fmap` and `comp` in this context and it makes probably little sense to build a `comp` version for methods in Javascript. I asked this question originally to obtain a deeper understanding of `fmap` and functors and the fact that functions are functors. –  Jun 13 '16 at 09:59
  • 1
    Since `fmap` is a method in the `Functor` type class, it is defined for various instances of `Functor` (such as lists, for example), of which functions are just one. That's why `fmap` has a different (more general) type than `.`. For functions, `fmap` indeed coincides with `.`. https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#line-638 (Sorry I was confused in my previous comment, which is already deleted.) – FPstudent Jun 13 '16 at 12:38
0

Forgive me if I'm mistaken but you seem to be confused about Functors in Haskell. The Functor type class in Haskell represents data types on which "map" (fmap to be more precise) is defined:

https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Functor.html

For instance, lists ([] in Haskell) are an instance of Functor, like:

    fmap (\x->x+1) [1,2,3]   -- returns [2,3,4]

As specified in the documentation above, it is true that fmap (f . g) should be equivalent to fmap f . fmap g (recall that . in Haskell means function composition, that is, (f.g) x equals f(g(x))). For instance, let

    f x = x + 1

and:

    g y = y * 2

Then

    fmap (f.g) [1,2,3]   -- equivalent to [(f.g) 1, (f.g) 2, (f.g) 3]

and

    (fmap f . fmap g) [1,2,3]   -- equivalent to (fmap f (fmap g [1,2,3]))

are equivalent and both return [3,5,7].

Arrays in JavaScript are already a Functor in this sense since they have map.

    const f = x => x + 1;
    const g = y => y * 2;
    const comp = f => g => x => f(g(x));
    const fmap_array = f => a => a.map(f);

    fmap_array (comp(f)(g)) ([1,2,3]); // [3,5,7]
    (comp (fmap_array(f)) (fmap_array(g))) ([1,2,3]); // [3,5,7]

Or, you can do this if you like:

    Array.prototype.fmap = function(f) { return this.map(f); }
    [1,2,3].fmap(f); // [2,3,4]
    [1,2,3].fmap(g); // [2,4,6]
    [1,2,3].fmap(comp(f)(g)); // [3,5,7]
    [1,2,3].fmap(g).fmap(f); // [3,5,7]

P.S.

Now I see what you meant in your question - functions (-> in Haskell) are also an instance of Functor, indeed defined as function composition fmap f g = (f . g):

https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Functor.html#control.i:ic:Functor:Functor:28

https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#line-638

For something similar in JavaScript,

    const fmap_func = f => g => comp(f)(g); // or "const fmap_func = comp;"!
    fmap_func(f)(g)(42); // 85

or, again if you like:

    Function.prototype.fmap = function(f) { return comp(f)(this); };
    g.fmap(f)(42); // 85
FPstudent
  • 831
  • 5
  • 9
  • 1
    When you combine two functions with `fmap` in Haskell you get the same result as with `(.)` ([see this question](http://stackoverflow.com/questions/27883414/difference-between-function-composition-operator-and-fmap)). And if you compose it (`fmap . fmap`) it behaves like `(.).(.)` ([see this answer](http://stackoverflow.com/a/5822395)). To better understand the why I tried to implement such an `fmap` in Javascript. But it turned out that this makes no sense, because `fmap` (combined with two functions) and `comp` are practically the same in Javascript. –  Jun 13 '16 at 12:11
  • Got it now - I've added a P.S. – FPstudent Jun 13 '16 at 12:15