我正在努力使用F#类型的签名表示法。例如,假设您有折叠功能:
let rec Fold combine acc l =
...
可能有此类签名:
('a -> 'b -> 'a) -> 'a -> list<'b> -> 'a
我将其读作
一个有三个参数的函数:
并返回'a。
但是,对于我的穴居人大脑来说,将其表达为
会更有意义('a, 'b -> 'a), 'a, list<'b> -> 'a
我确信有一个语义原因,为什么参数用箭头与函数返回类型完全相同的方式分隔,但不知何故我错过了它并且在书籍/文章中找不到明确的解释。每当我看到类型签名时,我都要花很多时间来理解它。我觉得我只是错过了让“解密”显而易见的那一小块难题。
有人可以赐教我吗?
答案 0 :(得分:15)
我确信存在语义原因 为什么参数是用a分隔的 箭头的方式完全相同 函数返回类型,但不知怎的,我是 错过了,没有发现清楚 到目前为止在书籍/文章中的解释。
您正在阅读第一个功能是否正确。对于即时解密,类型签名表示如下:
val functionName = inputType1 -> inputType2 -> ... -> inputTypeN -> returnType
通常,箭头符号表示功能可以进行。
// val add4 : int -> int -> int -> int -> int
let add4 a b c d = a + b + c + d;;
// val f : (int -> int)
let f = add4 1 2 3 // returns (int -> int) waiting for last argument
因为函数是curry,你可以在技术上写这样:
// val add4 : int -> int -> int -> int -> int
let add4 = (fun a -> (fun b -> (fun c -> (fun d -> a + b + c + d))));;
// val f : (int -> int)
let f = fun x -> add4 1 2 3 x
如果您考虑一下,add4
签名就相当于:
val add4 : int -> (int -> (int -> (int -> int) ) )
我相信我们使用箭头符号,因为当我们明确地讨论如上所示的参数时,它类似于函数的结构。
答案 1 :(得分:6)
签名是以这种方式编写的,因为所谓的Currying。稍微更准确的描述函数的方法是它需要一个('a
的函数并将函数从'b
返回到'a
)并返回一个函数'a
并将函数从list<'b>
返回到'a
。因此,类型签名可以重写为
('a -> 'b -> 'a) -> ('a -> (list<'b> -> 'a))
答案 2 :(得分:5)
您可以在F#中编写一个类似的函数,其类型与您提议的类型相同(但在F#中它将被写为('a * 'b -> 'a) * 'a * list<'b> -> 'a
。但是,现有函数的优点是它很容易部分应用它只提供参数的前缀。例如:
let sum = List.fold (+) 0
使用你的定义,你必须写
let sum l = List.fold((fun (x,y) -> x + y), 0, l)
答案 3 :(得分:2)
原因在于功能编程中每个函数实际上只有一个参数。
因此,假设您有一个名为Sum的函数:
int -> int -> int
需要2个int并返回一个int。现在,如果通过传递一个int来调用此函数,则不会出现任何编译器错误,而返回值的类型为int - &gt; INT。所以你看到这个箭头符号符合这种行为。此行为称为Currying。 查看:http://en.wikipedia.org/wiki/Currying