如何实现一个仿函数,使 map 可以应用于两个函数?

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

在Haskell中可以对两个函数应用fmap,这基本上就是函数组合。您甚至可以组合 fmap 以启用具有更高元数 (fmap . fmap) 的函数组合。

这是可行的,因为函数是函子。

如何在 Javascript 中实现这样的仿函数(或适当的 map 方法)?

这是我目前尝试过的方法:

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;

这适用于正常的函数组合:

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

但是,它不适用于 map 的组合版本:

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

我认为我太适应了 Javascript 中仿函数的传统实现方式。作为仿函数的函数与 list 或 maybe 的行为不同。

在Haskell中,一切都是函数。在你的 javascript 中,你的一些函数被表示为带有 .x() 方法的 funcs,还有一些是原生的 Functions。那不行。

以下是三种方法:

const sub = y => x => x - y;
const sqr = x => x * x;
const inc = x => x + 1;
const comp = f => g => x => f(g(x));
  • 纯函数,无方法。

    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
    
  • 扩展本机 Functions,使用 fmap 作为方法:

    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
    
  • 构建您自己的函数类型(也在 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
    

如果我弄错了,请原谅我,但您似乎对 Haskell 中的 Functor 感到困惑。 Haskell中的Functor类型class表示定义了"map"(更准确地说是fmap)的数据类型:

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

例如,列表(Haskell 中的[])是Functor 的实例,例如:

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

正如上面文档中所指定的那样,fmap (f . g) 确实应该等同于 fmap f . fmap g(回想一下 Haskell 中的 . 表示函数组合,即, (f.g) x 等于 f(g(x)))。例如,让

    f x = x + 1

和:

    g y = y * 2

然后

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

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

是等价的 return [3,5,7].

Array 在 JavaScript 中已经是 Functor,因为它们有 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]

或者,如果您愿意,可以这样做:

    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.

现在我明白你在问题中的意思了——函数(Haskell中的->)也是Functor的一个实例,确实定义为函数组合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

对于 JavaScript、

中的类似内容
    const fmap_func = f => g => comp(f)(g); // or "const fmap_func = comp;"!
    fmap_func(f)(g)(42); // 85

或者,如果您愿意,可以再说一遍:

    Function.prototype.fmap = function(f) { return comp(f)(this); };
    g.fmap(f)(42); // 85