撰写功能签名

Jyo*_*Pal 1 javascript haskell functional-programming composition function-composition

我已经读过g :: A - > B和f :: B - > C的组成,发音("f由g组成"),导致另一个函数(箭头)来自A - > C.这可以是更正式地表达为

f•g = f(g)= compose ::(B - > C) - >(A - > B) - >(A - > C)

上述组成也可以定义如下吗?请澄清.在这种情况下,compose函数采用相同的两个函数f和g并从A - > C返回一个新函数.

f•g = f(g)= compose ::((B - > C),(A - > B)) - >(A - > C)

lef*_*out 8

首先,我们需要做一些正确的事:

  • f ? g意味着完全不同的东西f(g).

    • 前者是一个函数,给定一个参数x,首先将它提供给g,然后将结果传递给f,并输出最终结果,即f(g(x)).
    • OTOH,f(g)意味着您可以立即将函数f应用于 g,而无需等待任何参数.(g恰好有一个函数类型,但在函数式语言中,函数可以像任何其他值/参数一样传递).

    除非你正在处理一些非常古怪的多态函数,否则其中一个将是错误的类型.例如,类型很好的组合可能是

    sqrt ? abs :: Double -> Double
    
    Run Code Online (Sandbox Code Playgroud)

    一个类型很好的应用程序可能(至少在Haskell中)

    map(sqrt) :: [Double] -> [Double]
    
    Run Code Online (Sandbox Code Playgroud)

    我假设您正在谈论以下内容f ? g.

  • 必须为函数本身提供类型签名,而不是为应用于某些参数的函数.这是一大堆人完全错误的东西:在f(x),你有一个功能f和一个参数x.但是,f(x)不是一个功能,仅这是将函数的值的结果值了!所以,你不应该写一些类似的东西f ? g :: ...(除非你实际上只讨论了由合成产生的类型).更好地写? :: ...(或者,在Haskell中(?) :: ...).

  • 功能箭头不是关联的.大多数数学家甚至可能不知道X -> Y -> Z应该是什么意思.在像Haskell这样的语言中它意味着什么可能实际上有点令人惊讶:

    X -> Y -> Z  ?  X -> (Y -> Z)
    
    Run Code Online (Sandbox Code Playgroud)

即这是一个函数的类型,它首先只接受一个类型的参数X.结果将再次成为一个函数,但只接受一个类型的参数Y.如果您愿意,此函数将具有X已内置的值(在所谓的闭包中,除非编译器对此进行优化).赋予它Y值也将允许函数实际完成其工作并最终产生Z结果.

在这一点上,你已经得到了答案,实际上是签名X -> Y -> Z并且(X, Y) -> Z基本上是等同的.重写这个过程称为currying.

特别是回答你的问题:大多数语言通常不做任何currying,所以签名((B -> C), (A -> B)) -> (A -> C)实际上更正确.它对应于您可以调用的函数

   compose(f,g)
Run Code Online (Sandbox Code Playgroud)

OTOH,咖喱签名(B -> C) -> (A -> B) -> (A -> C)意味着你需要逐一提出论据:

   compose(f)(g)
Run Code Online (Sandbox Code Playgroud)

只有像Haskell这样的语言才是标准样式,但你不需要那里的parens:在Haskell中解析所有以下内容

   compose(f)(g)
   compose f g
   (compose) f g
   (.) f g
   f . g
Run Code Online (Sandbox Code Playgroud)

其中.实际上是在所述组合物中操作,其作为可以从文档看到具有类型

(.) :: (b -> c) -> (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)