【问题标题】:How to read F# type signatures?如何阅读 F# 类型签名?
【发布时间】:2011-06-25 00:30:31
【问题描述】:

我正在努力使用 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

我确定参数用箭头分隔的方式与函数返回类型完全相同是有语义原因的,但不知何故我错过了它,到目前为止还没有在书籍/文章中找到明确的解释.每次我看到一个类型签名时,我都必须停下来花很多时间来理解它。我觉得我只是错过了使“解密”显而易见的那一小块拼图。

谁能赐教?

【问题讨论】:

    标签: f#


    【解决方案1】:

    我确定有语义上的原因 为什么参数用一个分隔 箭头的方式与 函数返回类型,但不知何故我 错过了,没有找到明确的 迄今为止在书籍/文章中的解释。

    您阅读的第一个功能是正确的。对于即时解密,类型签名表示如下:

    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
    

    因为函数是柯里化的,所以技术上可以这样写:

    // 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) ) )
    

    我相信我们使用箭头表示法是因为它类似于上面显示的显式 curry 参数时的函数结构。

    【讨论】:

    • 解释清楚,谢谢。读完之后,我想知道我以前怎么没看到...
    • 我不认为这是我们使用箭头符号(在类型中)的原因。毕竟,在 lambda 演算中,我们在类型语法中使用“->”,但使用“.”。在术语语法中。 [当然,我们在 F# 中使用 -> 可能是真的,因为我们在类型和 .已被占用。]
    【解决方案2】:

    签名以这种方式编写是因为所谓的Currying。描述你的函数的一种稍微更准确的方法是它接受一个(接受'a并从'b返回一个函数到'a的函数)并返回一个接受'a并返回一个函数从list&lt;'b&gt;'a 的函数。因此,类型签名可以重写为

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

    【讨论】:

    • 和kvb一样,很好的解释,谢谢-不幸的是我只能接受一个答案
    【解决方案3】:

    您可以在 F# 中编写一个类似的函数,该函数的类型与您提议的类似(但在 F# 中,它将被写为 ('a * 'b -&gt; 'a) * 'a * list&lt;'b&gt; -&gt; 'a。但是,现有函数的优点是它很容易通过以下方式部分应用它只提供参数的前缀。例如:

    let sum = List.fold (+) 0
    

    使用你的定义,你必须写

    let sum l = List.fold((fun (x,y) -> x + y), 0, l)
    

    【讨论】:

      【解决方案4】:

      原因是在函数式编程中,每个函数实际上只有一个参数。

      假设您有一个名为 Sum as 的函数:

      int -> int -> int
      

      它需要 2 个 int 并返回 1 个 int。现在,如果您只通过传递一个 int 来调用此函数,您将不会得到任何编译器错误,而是返回值将是 int -> int 类型。所以你会看到这个箭头符号符合这种行为。这种行为称为柯里化。 签出:http://en.wikipedia.org/wiki/Currying

      【讨论】:

      • 很好的例子,对上述答案的完美补充!但是 schönfinkeling 是一个更好的名字 :-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-05
      • 1970-01-01
      相关资源
      最近更新 更多