在与 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
->
的优点style 是我可以定义类型别名,例如// 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限制吗?还是有什么特殊用途? 为什么不用
->
定义所有显式类型?而不是 :
?
最佳答案
我宁愿在这里使用术语类型注释而不是“显式类型声明”。后者表明发生的事情比实际情况更多 - 类型注释只是为了指导编译器(有时是程序员),仅此而已。
这里没有“两种风格”。只有一种风格。您有一个想要注释的值,您可以在它后面加上 :
和类型签名。 ->
只是函数类型名称的一部分。
let OK2 : string -> Context -> Async<Context option> = fun content context ->
...
在上面的片段中,您有一个
let
绑定(bind) OK2
, 你用类型 string -> Context -> Async<Context option>
注释它你为它提供了一个值——它恰好是一个函数。比较一个简单值的绑定(bind),这是完全相同的语法:let simpleValue : int = 42
然而,这并不是考虑函数的一种特别方便的方式。与值相比,将它们视为接受一些参数并返回结果的实体更方便。这就是速记声明所捕获的内容。但这并不是您似乎暗示的完全不同的独立风格,否则这是不可能的:
let OK2 (content: string): Context -> Async<Context option> = fun context ->
...
你是对的,你不能使用
WebPart
这里的别名。考虑到您在此处不再具有别名类型的值,这不足为奇。我同意你的观点,以这种方式使用函数类型别名是有值(value)的。它可以使您的函数乍一看更加统一,从而提高 API 的可读性和可发现性 - 但与此同时,您会通过使各个函数的实现更加复杂而付出代价。总是需要权衡取舍。
关于f# - 为什么 F# 有两种显式类型声明?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39516766/