【问题标题】:Type of addition (+) in F#F# 中的加法类型 (+)
【发布时间】:2011-06-08 21:26:04
【问题描述】:

我刚刚了解到 OCAML 必须有一个 . 后缀才能进行浮点运算。一个例子是3. +. 4.,它等于7.(浮点数)。但是,F# 以相同的方式处理浮点和整数运算,因此 3 + 4 (int) 和 3. + 4. (float) 都有效。

F# 将 + 自然分配给 int,因此 let add a b = a + b 的类型为 int -> int -> int。确实(+) 给了我val it : (int -> int -> int) = <fun:it@6-1>

这导致以下顺序,我认为这很违反直觉:

> 3. + 4.;;
val it : float = 7.0
> (+);;
val it : (int -> int -> int) = <fun:it@8-2>

所以我的问题是:“重载”是由编译器中的特殊机制/案例完成的,还是语言范围内的事情,所以我可以定义一个名为 add 的函数(或其他任何东西)对整数有一个定义,对浮点数(或任何其他类型)有一个定义。

【问题讨论】:

  • 您可能对此感兴趣:stackoverflow.com/questions/4732672/…
  • @Daniel 编译时类型约束。很好,谢谢 :) 但是,问题仍然存在:这是在 F# 中完成的吗?
  • 不确定我是否关注。泛型函数只能写成inline,如果那是你要问的。

标签: types f# ocaml inline


【解决方案1】:

简而言之,F# 具有通过 inline 关键字和“静态成员约束”的临时重载机制。内置数学运算符还有一些特殊的魔力,它神奇地假定类型 int 没有其他约束。 (+) 几乎是所有 F# 中最特别/最神奇的东西,所以它不能很好地介绍语言/类型系统。

一般来说,“重载”对于静态类型的类型推断语言来说是很困难的。 F#在这里的选择非常务实。 OCaml 做了一个不同的、简单的、实用的事情(没有重载)。 Haskell 做了一个不同的、复杂但优雅的事情(类型类)。它们在语言/库设计空间中都是合理的。

【讨论】:

  • 谢谢,你在评论中的链接和这个描述就是我所需要的:)
【解决方案2】:

重载的函数(和运算符)必须在 F# 中标记为 inline。这是因为它们依赖于explicit member constraints。这些约束在编译时解决。函数let inline add a b = a + b 的类型为'a -&gt; 'b -&gt; 'c (requires member (+)),其中+ 是静态函数/运算符。你不能在 C# 中做到这一点;它没有静态成员约束。

let inline add a b = a + b
add 1 2 //works
add 1.0 2.0 //also works

【讨论】:

  • +1 Cool :) 我从函数的类型中看到它使用编译类型约束。然而,这并不能完全回答这个问题。 (+) 真正适用于两者时的定义是什么?
  • 类似:'a -&gt; 'b -&gt; 'c (requires member (+)) 这意味着,它适用于任何具有静态成员 (+) 的类型。
  • 什么是(+)? :) 它告诉我它是int -&gt; int -&gt; int 但它适用于浮动,为什么?如果不清楚我的意思,我很抱歉:/
  • 你可以在这里看到代码:github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/… 很血腥,你不想知道细节。其中一些是内置数学运算符的特定编译器魔法。更一般地说,您可以使用inline 和静态成员约束在用户代码中做类似的鸭式输入,但这也是血腥的,您不想知道细节。简短的回答是:“魔法”。
  • @lass:它的类型为int -&gt; int -&gt; int,除非你将它标记为inline。所以,是的,F# 似乎默认选择 int,除非它可以根据使用情况推断出更好的类型(例如,如果您对函数的初始调用使用浮点参数)。
【解决方案3】:

除了布赖恩的回答和链接:

https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/prim-types.fs

我在代码中找到了一些定义:

let inline (+) (x:int) (y:int) = (# "add" x y : int #)

let inline (+) (x: ^T) (y: ^U) : ^V = 
     AdditionDynamic<(^T),(^U),(^V)>  x y 
     when ^T : int32       and ^U : int32      = (# "add" x y : int32 #)
     when ^T : float       and ^U : float      = (# "add" x y : float #)
     when ^T : float32     and ^U : float32    = (# "add" x y : float32 #)
     ...

AdditionDynamic 在此处定义(大量静态内容和 CIL): https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/prim-types.fs#L2374

有趣的东西:

(# "add" 1 2 : int32 #)

有效,并给出 3 作为输出(带有警告说你不应该这样做。)

【讨论】:

  • 是的,当没有 .NET API 时,FSharp.Core 库具有访问底层 IL 结构的魔力。你不应该在你自己的代码中使用(# ... #) 的东西,编译器不会完全拒绝它是一个错误(编译器的未来版本会完全拒绝它)。
  • @Brian:为什么我们不能编写内联 IL?这不是错误;这是一个功能。 :-)
猜你喜欢
  • 1970-01-01
  • 2019-08-18
  • 2012-11-05
  • 1970-01-01
  • 2018-08-20
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多