什么是一个不是身份函数的endofunctor的好例子? [英] What would be a good example of an endofunctor that is not the identity functor?

查看:210
本文介绍了什么是一个不是身份函数的endofunctor的好例子?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Frisby教授推出可组合功能JavaScript 引入了身份仿函数:

  const Box = x => 
({
map:f => Box(f(x)),
fold:f => f(x)//用于测试
})

我花了很多时间理解函数,以及为什么上面的JavaScript代码实际上是身份仿函数。所以我想我会改变它来得到一个不是身份函数的真正的函子。我想出了这个:

  const Endo = x => 
({
map:f => Endo(f(x).split('')),
fold:f => f(x).split('') //用于测试
))

我的推理是使用Box, Id_Box:Box - > Box Id_Box f = f 。 Endo也会映射到它本身,但是 Endo(f):Endo(x) - > Endo(y)(if f:x - > y )。

我在正确的轨道上?

编辑:
用更通用的<$ c替换字符串

解决方案

正如这个答案,对于我们作为程序员的目的,我们可以将所有函子当作endofunctor,所以不要太在意这些差异。 / p>

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


  1. 可支持映射操作的数据结构(您的示例中为Box)

  2. (请考虑 Array.prototype.map


  3. ...和组合: xs.map(f).map(g)=== xs.map(f。g)其中是函数组合。

就是这样。没有更多,不少。看看你的 Box ,它是一个具有 map 函数(检查1& 2)的数据结构,并且该映射函数看起来应该尊重身份和组合(检查3和4)。所以它是一个函子。但它并没有做任何事情,这就是为什么它是身份仿函数。 fold 函数并不是必须的,它只是提供了一种'解包'方块。



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

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

//在这里,我们需要对数组进行字符串化,因为JS会比较
// ref而不是value。在第一次为
//简洁之后,我省略了它,但知道这是必要的。
[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

所以当我们 map a函数通过函子(数组)返回另一个相同函子的实例(一个新的数组),该函数应用于函子(数组)的任何东西。



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

pre $ 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}

您可以继续测试该功能是否准确尽可能在JavaScript中)支持身份和组合以满足函子法则。

In Professor Frisby Introduces Composable Functional JavaScript the identity functor was introduced:

const Box = x => 
   ({ 
       map:  f => Box(f(x)),
       fold: f => f(x)           // for testing
   })

I spent the better part of the day understanding functors and why the above JavaScript code is actually the identity functor. So I thought I would alter it to get a "real" functor that is not the identity functor. I came up with this:

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

My reasoning is that with Box, Id_Box: Box -> Box and Id_Box f = f. Endo would also map to itself but Endo(f): Endo(x) -> Endo(y) (if f: x -> y).

Am I on the right track?

EDIT: Replaced string with the more generic x as it was in the original examples.

解决方案

As pointed out in this answer, for our purposes as programmers we can treat all functors as endofunctors so don't get too caught up on the differences.

As for what a functor is, in brief it is

  1. a data structure (Box in your example)
  2. that can support a mapping operation (think Array.prototype.map)
  3. and that mapping operation respects identity: xs === xs.map(x => x)
  4. ...and composition: xs.map(f).map(g) === xs.map(f . g) where . is function composition.

That's it. No more, no less. Looking at your Box, it's a data structure that has a map function (check 1 & 2) and that map function looks like it should respect identity and composition (check 3 & 4). So it's a functor. But it doesn't do anything, which is why it's the identity functor. The fold function isn't strictly necessary, it just provides a way to 'unwrap' the box.

For a useful functor, let's look at JavaScript arrays. Arrays actually do something: namely they contain multiple values rather than just a single one. If an array could only have one element, it'd be your Box. For our purposes we'll pretend that they can only hold values of the same type to simply things. So an array is a data structure, that has a map function, that respects identity and composition.

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

So when we map a function over a functor (array) we get back another instance of the same functor (a new Array) with the function applied to whatever the functor (array) was holding.

So now lets look at another ubiquitous JavaScript data structure, the object. There's no built in map function for objects. Can we make them a functor? Assume again that the object is homogenous (only has keys to one type of value, in this example 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 }

And you can continue on to test that the function accurately (as far as is possible in JavaScript) supports identity and composition to satisfy the functor laws.

这篇关于什么是一个不是身份函数的endofunctor的好例子?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆