什么是一个不是身份函子的endofunctor的好例子?

tor*_*tte 1 javascript functional-programming category-theory

在Frisby 教授介绍可组合功能JavaScript中引入了身份仿函数:

const Box = x => 
   ({ 
       map:  f => Box(f(x)),
       fold: f => f(x)           // for testing
   })
Run Code Online (Sandbox Code Playgroud)

我花了大部分时间来理解函子以及为什么上面的JavaScript代码实际上是身份函子.所以我想我会改变它以获得一个不是身份函子的"真正的"函子.我想出了这个:

const Endo = x =>
   ({ 
       map:  f => Endo(f(x).split('')),
       fold: f => f(x).split('') // for testing
   })
Run Code Online (Sandbox Code Playgroud)

我的理由是用Box,Id_Box: Box -> BoxId_Box f = f.远藤也会映射到自己,但Endo(f): Endo(x) -> Endo(y)(如果f: x -> y).

我是在正确的轨道上吗?

编辑:替换为原始示例中string的更通用x.

Jar*_*ith 5

正如在这个答案中指出的那样,对于我们作为程序员的目的,我们可以将所有函子视为endofunctors,因此不要过于关注差异.

至于仿函数什么,简而言之就是这样

  1. 数据结构(示例中为Box)
  2. 可以支持映射操作(想想Array.prototype.map)
  3. 并且映射操作尊重标识: xs === xs.map(x => x)
  4. ...和组成:功能组成xs.map(f).map(g) === xs.map(f . g)在哪里..

而已.不多也不少.看看你的Box,它是一个具有map函数的数据结构(检查1和2),并且map函数看起来应该尊重身份和组合(检查3和4).所以这是一个算子.但它没有任何事情,这就是为什么它是身份函子.该fold功能不是绝对必要的,它只是提供了一种"打开"盒子的方法.

对于有用的算子,让我们看一下JavaScript数组.数组实际上做了一些事情:即它们包含多个值而不仅仅是一个值.如果一个数组只能有一个元素,那就是你的Box.出于我们的目的,我们假装他们只能将相同类型的值保存到简单的事物中.所以数组是一个数据结构,它有一个映射函数,它尊重身份和组合.

let plus = x => y => x + y;
let mult = x => y => x * y;
let plus2 = plus(2);
let times3 = mult(3);
let id = x => x;
let compose = (...fs) => arg => fs.reverse().reduce((x, f) => { return f(x) }, arg);  

// Here we need to stringify the arrays as JS will compare on 
// ref rather than value. I'm omitting it after the first for
// brevity, but know that it's necessary.
[1,2,3].map(plus2).toString() === [3,4,5].toString(); // true
[1,2,3].map(id) === [1,2,3]; // true
[1,2,3].map(plus2).map(times3) === [1,2,3].map(compose(times3, plus2)); // true
Run Code Online (Sandbox Code Playgroud)

因此,当我们map在函数(数组)上运行函数时,我们会返回同一仿函数的另一个实例(一个新的数组),该函数应用于函数(数组)所持有的任何函数.

所以现在让我们看看另一个无处不在的JavaScript数据结构,即对象.map对象没有内置函数.我们可以让它们成为一个仿函数吗?再次假设对象是同质的(只有一种类型的值的键,在本例中为Number):

let mapOverObj = obj => f => {
  return Object.entries(obj).reduce((newObj, [key, value]) => {
    newObj[key] = f(value);
    return newObj;
  }, {});
};

let foo = { 'bar': 2 };
let fooPrime = mapOverObj(foo)(plus2); // { 'bar': 4 }
Run Code Online (Sandbox Code Playgroud)

并且您可以继续测试该函数(尽可能在JavaScript中)支持身份和组合以满足仿函数法则.