在玩过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限制吗?还是有特殊目的? 为什么不用->
而不是:
定义所有显式类型?
答案 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
第二个更短,阅读更容易。