理解Substitution /`ap`类型签名和不同实现(函数式编程)的混乱

时间:2018-02-11 19:52:52

标签: functional-programming ramda.js category-theory fantasyland sanctuary

我是函数式编程的学生,对不起,如果我的问题听起来很奇怪 - 我试图围绕函数的给定类型签名以及如何实现它们。

查看ap(替换)的签名

https://gist.github.com/Avaq/1f0636ec5c8d6aed2e45

(a → b → c) → (a → b) → a → c

在这里给出

const S = f => g => x => f(x)(g(x));

我认为我理解。 f是一个函数,它使用两个参数ab并返回cg是一个需要a并返回b的函数。因此g(a)会返回b,因此f(a)(b)可以写为f(a)(g(a)),返回c

g(a)b

的替代品

好的,现在我正在寻找一个仍然有意义的不同实现:

https://github.com/sanctuary-js/sanctuary-type-classes/tree/v7.1.1#ap--applyf--fa-bfa---fb

ap(Identity(Math.sqrt), Identity(64))

类型签名

  1. (f (a -> b), f a) -> f b
  2. 似乎与

    相似
    1. (a → b → c) → (a → b) → a → c
    2. 使用a = f重写第二个,b = a,c = b我得到

      1. (f -> a -> b) -> (f -> a) -> f -> b
      2. 假设ap有两个参数,第一个f可能是包含函数a -> b的一些仿函数,而第二个f包含a的仿函数{1}}返回一个仿函数,用第一个仿函数函数替换结束点b,然后给出包含a的仿函数。

        好的,退一步说,这两件事看起来大不相同,我无法理解他们是如何以某种方式说同样的事情。

        1. const S = f => g => x => f(x)(g(x))

        2. ap(Identity(Math.sqrt), Identity(64))

        3. 根据我的理解,ap(F(g),F(a))可以表达为F(a).map(g),再次,我仍然很难等同于const S = f => g => x => f(x)(g(x))。也许我误解了一些事情。

          ...也许我的误解与ap的表达方式有关,以及它与f => g => x => f(x)(g(x))的关系如何,因为我可以看到它们如何表达相同的签名,但我不认为它们是同样的事情。

          任何能够在这里提供认知帮助的人,我都会非常感激

2 个答案:

答案 0 :(得分:5)

ap是转换的名称,它在大量容器类型(称为Applicative Functors)上的行为方式相同。一个这样的容器类型是Function:它可以被视为其返回值的容器

你在我的要点中找到的S组合来自无类型的Lambda微积分,并且是一个函数的转换。它恰好也是函数Applicative Functor的有效实现,它恰好是Ramda和Sanctuary的首选实现。这就是您可以将ap用作S

的原因

要了解ap S的方式,让我们看一下ap的签名:

Apply f => (f (a -> b), f a) -> f b

让我们通过调整函数来摆脱逗号。这应该使接下来的步骤更容易理解:

Apply f => f (a -> b) -> f a -> f b

Apply f部分显示,我们在哪里看到f a,我们可以使用包含a的Applicative Functor容器。让我们通过将f替换为(Function x)来专门为Function容器设置此签名。 x是函数的输入,接下来是输出

(Function x) (a -> b) -> (Function x) a -> (Function x) b

其内容如下:给出xab的函数的函数,以及从xa的函数,将函数从x返回到b

由于构造函数关联的工作方式,我们可以删除Function x周围的括号:

Function x (a -> b) -> Function x a -> Function x b

另一种写Function a b的方法是使用箭头符号:(a -> b),所以在下一步中我们会这样做:

(x -> (a -> b)) -> (x -> a) -> (x -> b)

最后我们可以再次摆脱额外的括号,发现它是我们的S组合器:

(x -> a -> b) -> (x -> a) -> x -> b
(a -> b -> c) -> (a -> b) -> a -> c

答案 1 :(得分:2)

首先,我认为没有简单的解释为什么无类型lambda演算中函数类型的applicative functor被称为替换。 AFAIK,Schönfinkel最初称这种组合器融合或融合功能。

为了专门化一般的applicative functor类型(f (a -> b), f a) -> f b(uncurried form),我们需要知道参数化类型变量f在函数类型的上下文中的确切表示。

因为每个仿函数applicative仿函数都在一个类型上进行参数化。但是,函数类型构造函数需要两种类型 - 一种用于参数,另一种用于返回值。要使函数成为(applicative)仿函数的实例,我们必须忽略返回值的类型。因此,f代表(a -> ),即。函数类型本身及其参数的类型。部分应用的函数类型构造函数的正确表示法实际上是前缀(->) a,所以让我们坚持这一点。

接下来,我将以咖喱形式重写常规应用类型,并将f替换为(->) r。我使用另一个字母来分隔来自其他类型变量的applicative的类型参数:

(f (a -> b), f a) -> f b
f (a -> b) -> f a -> f b // curried form

// substitution
(->) r (a -> b) -> (->) r a -> (->) r b // prefix notation
(r -> a -> b) -> (r -> a) -> (r -> b) // infix notation

// omit unnecessary parenthesis
(r -> a -> b) -> (r -> a) -> r -> b

这正是S组合子的类型。