为什么F#有两种类型的显式类型声明?

时间:2016-09-15 17:12:17

标签: f#

在玩过Idris后,我成为了使用显式类型加强我的F#程序的声明性自我文档的忠实粉丝,更经常在我的函数上明确地编写类型并使用类型别名。

使用显式类型定义我的函数让我(重新)发现F#实际上有两种函数显式类型。使用:有标准的let函数样式,并且使用->有lambda函数。

例如

// explicitly typed function using the classical let ':' style
let OK1 (content : string) (context : Context) : Async<Context option> =
    { context with Response = { Content = content; StatusCode = 200 } }
    |> Some
    |> async.Return
// explicitly typed function using the '->' style
let OK2 : string -> Context -> Async<Context option> = fun content context ->
    { context with Response = { Content = content; StatusCode = 200 } }
    |> Some
    |> async.Return

关于->样式的好处是我可以定义类型别名,例如

// type alias defining a webpart
type WebPart = Context -> Async<Context option>
// using the type alias in the declaration of an explicitly typed function
let OK2 : string -> WebPart = fun content context ->
    { context with Response = { Content = content; StatusCode = 200 } }
    |> Some
    |> async.Return

我不认为可以使用:样式声明和使用相同类型的别名吗?

我很困惑为什么F#有两种在函数上声明显式类型的样式。这是某种.Net限制吗?还是有特殊目的? 为什么不用->而不是:定义所有显式类型?

2 个答案:

答案 0 :(得分:2)

我宁愿在这里使用术语类型注释而不是&#34;显式类型声明&#34;。后者表明还有比实际更多的东西 - 类型注释只是为了指导编译器(有时是程序员),仅此而已。

没有&#34;两种风格&#34;这里。只有一种风格。您有一个要注释的值,您可以使用:和类型签名进行注释。 ->只是函数类型名称的一部分。

let OK2 : string -> Context -> Async<Context option> = fun content context ->
    ...

在上面的代码段中,您有一个let绑定OK2,您可以使用类型string -> Context -> Async<Context option>对其进行注释,并为其提供一个值 - 恰好是一个函数。比较一个简单值的绑定,这是完全相同的语法:

let simpleValue : int = 42

然而,这不是考虑功能的特别方便的方式。而不是价值观,将它们视为接受一些参数并返回结果的实体更方便。这就是速记宣言所捕获的内容。但它似乎不是一个完全不同的,独立的风格,否则这是不可能的:

let OK2 (content: string): Context -> Async<Context option> = fun context ->
    ...

您是对的,因为您无法在此处使用WebPart别名。考虑到你不再拥有别名类型的值,这不应该是令人惊讶的。

我同意你的看法,以这种方式使用函数类型别名是有价值的。它可以使您的功能在第一眼看上去更加统一,从而提高了API的可读性和可发现性 - 但与此同时,您通过使各个功能的实现更加复杂来付出代价。总是需要权衡利弊。

答案 1 :(得分:1)

两个选项完全等效,它们的含义完全相同,编译方式完全相同。第一个选项作为一种快捷方式存在。比较两者:

let f = fun a b -> a + b
let f a b = a + b

第二个更短,阅读更容易。