如何阅读F#类型签名?

时间:2011-06-24 15:41:18

标签: f#

我正在努力使用F#类型的签名表示法。例如,假设您有折叠功能:

let rec Fold combine acc l =
...

可能有此类签名:

('a -> 'b -> 'a) -> 'a -> list<'b> -> 'a

我将其读作

一个有三个参数的函数:

  • 一个带有'a,a'b并返回'
  • 的函数
  • a'a
  • “b
  • 的列表

并返回'a。

但是,对于我的穴居人大脑来说,将其表达为

会更有意义
('a, 'b -> 'a), 'a, list<'b> -> 'a

我确信有一个语义原因,为什么参数用箭头与函数返回类型完全相同的方式分隔,但不知何故我错过了它并且在书籍/文章中找不到明确的解释。每当我看到类型签名时,我都要花很多时间来理解它。我觉得我只是错过了让“解密”显而易见的那一小块难题。

有人可以赐教我吗?

4 个答案:

答案 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