【问题标题】:Why does F# have two types of explicit type declarations?为什么 F# 有两种显式类型声明?
【发布时间】:2017-01-23 19:31:54
【问题描述】:

在玩过 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

-&gt; 样式的好处是我可以定义类型别名,例如

// 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限制吗?还是有什么特殊用途? 为什么不用-&gt; 而不是: 来定义所有显式类型?

【问题讨论】:

    标签: f#


    【解决方案1】:

    我宁愿在这里使用术语类型注释,而不是“显式类型声明”。后者表明发生的事情比实际情况更多 - 类型注释只是为了指导编译器(有时是程序员),仅此而已。

    这里没有“两种风格”。只有一种风格。您有一个要注释的值,然后使用 : 和类型签名。 -&gt; 只是函数类型名称的一部分。

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

    在上面的 sn-p 中,你有一个 let 绑定 OK2,你用类型 string -&gt; Context -&gt; Async&lt;Context option&gt; 注释它,并为它提供一个值 - 这恰好是一个函数。比较一个简单值的绑定,这是完全相同的语法:

    let simpleValue : int = 42
    

    然而,这并不是考虑函数的一种特别方便的方式。与值相比,将它们视为接受一些参数并返回结果的实体更方便。这就是速记声明所捕获的内容。但这并不是您似乎暗示的完全不同的独立风格,否则这是不可能的:

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

    你说得对,你不能在这里使用 WebPart 别名。考虑到您在此处不再具有别名类型的值,这不足为奇。

    我同意你的观点,以这种方式使用函数类型别名是有价值的。它可以使您的函数乍一看更加统一,从而提高 API 的可读性和可发现性 - 但与此同时,您会通过使各个函数的实现更加复杂而付出代价。总是需要权衡取舍。

    【讨论】:

      【解决方案2】:

      这两个选项是完全等价的,它们的意思完全一样,编译的方式也完全一样。第一个选项作为一种捷径存在。比较两者:

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

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

      【讨论】:

      • 我同意本案。尽管该示例似乎与显式类型无关。我最感兴趣的是为什么 lambda 样式和“普通”样式之间的显式类型定义不同。似乎使用“正常”样式,您失去了在显式类型声明中使用类型别名(例如 type WebPart = Context -&gt; Async&lt;Context option&gt;)的能力。
      • 比较 stackoverflow.com/questions/39394254/… 。有鉴于此,“这两个选项完全等价”的说法可能不成立。不过不确定。
      • 是的,这个概念确实适用于这种情况。 foo是一个正则函数,其定义等价于let foo a b = ...,但bar不同。
      • @BitTickler 如果将f 直接声明为fun ...function ...,则该语句确实成立。只有当f 的表达式不是直接匿名函数时,值限制才会起作用,因为只有这样才会有副作用的风险。
      猜你喜欢
      • 1970-01-01
      • 2021-09-28
      • 1970-01-01
      • 1970-01-01
      • 2015-03-11
      • 1970-01-01
      • 1970-01-01
      • 2011-04-16
      • 2020-05-12
      相关资源
      最近更新 更多