如何正确使用尖头仿函数

时间:2016-08-27 10:02:48

标签: javascript functional-programming functor

我试图熟悉JavaScript中的函数式编程。我只是read指针函数是:

  

具有of函数的对象,可将任何单个值放入其中。

     

ES2015添加Array.of使数组成为一个有针对性的算符。

我的问题是究竟是什么意思"单一价值"?

我想制作一个Functor / Container(就像在https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch8.html中),它将给定尺寸(宽度,高度)的网格保存为一维数组,并允许我对其进行转换。作为一个普通的对象,我会把它存储为{ width: 2, height: 2, list: [1, 2, 3, 4] },但我想把它放在一个仿函数中,我不知道该怎么做。

我知道使用像这样的尖头仿函数存储单个值是完全没问题的:

Container.of(47)

但是可以使用object作为值,假设对象是"单个值":

Grid.of({ width: 2, height: 2, list: [1, 2, 3, 4] })

或者甚至喜欢这样:

Grid.of(2, 2, [1, 2, 3, 4])

2 个答案:

答案 0 :(得分:7)

遗憾的是,https://github.com/hemanth/functional-programming-jargon中的解释并不十分准确。

指向仿函数实际上是一个仿函数F以及为每种类型 of定义的函数a,并且发送类型为x的{​​{1}}值aof(x)类型的值F a。在Hindley-Milner signature中,它看起来像这样:

of :: a -> F a

例如,数组仿函数指向of = x => [x],为任何类型x的每个值a定义。

此外,函数of(或更准确地说,函数of的集合,因为每种类型a都有一个函数)必须是从身份函子到{的自然转换{1}}。这意味着F应用于函数的值等于of应用于函数的参数,然后映射到函数上:

of

例如,在Array示例中,您有

of(f(x)) === of(x).map(f)

所以[f(x)] === [x].map(f), 确实是一种自然的转变。

但您也可以将x => [x]重新定义为

of

,即使of = x => [x, x] [f(x), f(x)] === [x, x].map(f) 方法保持不变,也会将Array转换为另一个尖头仿函数。 (请注意,在每种情况下,您只能获得非常特殊的数组作为map的值。)

但是,您无法将of(x)定义为例如

of

现在

of = x => [x, 0]
[f(x), 0] !== [x, 0].map(f)

完全没问题,并将包裹的对象返回到var grid = Grid.of({ width: 2, height: 2, list: [1, 2, 3, 4] }) 。然后,您可以将Grid与常规函数grid从普通对象映射到普通对象,结果与应用f并包装到f相同,因为自然转化法。请注意,通过这种方式,您还可以使用Grid甚至Grid.of之类的任何其他值来调用Grid.of({width: 2})。或者,您可以限制定义Grid.of(2)的类型,然后该值必须只是您允许的类型。

这个有点棘手:

Grid.of

这会将Grid.of(2, 2, [1, 2, 3, 4]) 应用于多个参数。由于Grid.of根据定义只是一个参数的函数,因此结果将是Grid.of,这可能不是您想要的。如果你真的想要提供所有值,你可能想写

Grid.of(2)

或者,您可以通过在内部将它们预先包装到数组中然后应用Grid.of([2, 2, [1, 2, 3, 4]]) 来将Grid.of扩展为多个参数。这实际上取决于你所追求的目标。

有关实际使用示例,请参阅例如here其中一个"无聊"任务是通过Grid.of从普通值定义的。另一方面,here是一个更有趣的任务包装一个你不会用Task.of得到的函数。但重要的是,两个任务都可以使用与两个示例中所示相同的统一接口。

另请注意,这些示例中没有使用任何应用仿函数,因此仍然使用尖头仿函数而不是应用。

溶液。

另请参阅https://github.com/MostlyAdequate/mostly-adequate-guide-it/blob/master/ch9.md#pointy-functor-factory以获得Pointed Functor的精彩介绍和实际使用。

答案 1 :(得分:3)

  

但是假设object是“单值”,可以使用object作为值:

是。 of应该使用任何值并将其放入容器中。一个对象肯定是这样一个单一的值。

  

Grid.of(2, 2, [1, 2, 3, 4])

没有。 of应该采用一个参数。如果你想在一个仿函数中放入多个值,先将它们放在另一个结构中,然后将该结构放在仿函数中,或者通过其函数(of)以外的其他结构构造仿函数。

  

Grid.of({ width: 2, height: 2, list: [1, 2, 3, 4] })

不,如果您希望返回输入,那么它将无效。 of应该按原样输入输入并围绕它包装结构。对于你的网格,它肯定会是这样的:

// Grid<A>
class Grid {
    // Int -> Int -> [A] -> Grid<A>
    constructor(w, h, vals) {
        assert(Number.isInteger(w) && Number.isInteger(h));
        this.width = w;
        this.height = h;
        const list = Array.from(vals);
        assert(list.length == w * h);
        this.list = list;
    }
    // Grid<A> -> (A -> B) -> Grid<B>
    map(f) {
        return new Grid(this.width, this.height, this.list.map(f));
    }
    // A -> Grid<A>
    static of(x) {
        return new Grid(1, 1, [x]);
    }
}

因此,上述调用将创建Grid个对象,而不是四个数字的网格。请注意,of不是构造仿函数实例的唯一方法,它只是从单个元素构造实例的方法。

请注意of作为Applicative的一部分是最重要的,对普通的Functors来说并不那么有趣。顺便说一下,如果你对函数式编程概念感兴趣,你也应该能够使Grid成为Monoid,Traversable和Monad - 见https://github.com/fantasyland/fantasy-land