为什么 F# 无法对列表元素进行类型推断?
Why F# fails to have type inference for list elements?
I was trying to put both int and float into a list construction:
> let l2=[1;2.0];;
let l2=[1;2.0];;
----------^^^
stdin(50,11): error FS0001: This expression was expected to have type
int
but here has type
float
>
好吧,如果我在 Haskell 中写入 let l3=[1,2.0],它可以构建包含所有 "fractional" 元素的列表。为什么 F# 不支持自动类型推断? F#是基于.net的,Int和Float都是Object类型吧?为什么 Int 和 Float 不能放在一个列表中,而有些元素会自动进行类型提升或转换?似乎 F# 的类型系统使得编写更通用的程序变得非常困难。
有什么提示吗?
F# 没有像 Haskell 这样的类型类。虽然它基于 .NET,但 int
和 float
之间唯一的共同类型是 obj
,并且 obj
不提供使您能够执行算术的运算符。
不过,您可以定义一个包含int
和float
值的列表:
let l : obj list = [1; 2.0]
不过,这接近于 无用,因为除非您尝试向下转换它们,否则您无法使用这些值做任何事情。它也是 不安全的 ,因为这样的列表很容易包含非数字值:
let l : obj list = [1; 2.0; "foo"]
然而,一切并没有丢失,因为您可以使用 inline
关键字让编译器找出各种输入所需的运算符:
type Direction = Left = -1 | Straight = 0 | Right = 1
let inline turn (x1, y1) (x2, y2) (x3, y3) =
let prod = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)
if prod > LanguagePrimitives.GenericZero then Direction.Left
elif prod < LanguagePrimitives.GenericZero then Direction.Right
else Direction.Straight
此函数具有以下类型:
val inline turn :
x1: ^a * y1: ^f -> x2: ^b * y2: ^g -> x3: ^j * y3: ^e -> Direction
when ( ^b or ^a) : (static member ( - ) : ^b * ^a -> ^c) and
( ^j or ^a) : (static member ( - ) : ^j * ^a -> ^i) and
( ^c or ^d) : (static member ( * ) : ^c * ^d -> ^l) and
( ^e or ^f) : (static member ( - ) : ^e * ^f -> ^d) and
( ^g or ^f) : (static member ( - ) : ^g * ^f -> ^h) and
( ^h or ^i) : (static member ( * ) : ^h * ^i -> ^k) and
( ^l or ^k) : (static member ( - ) : ^l * ^k -> ^m) and
^m : (static member get_Zero : -> ^m) and ^m : comparison
它声明 ^a
和 ^b
是支持静态运算符 -
的类型,当使用时,returns 类型 ^c
,支持静态运算符*
等。
这个例子来自my convex hull example.
它可以与 int
和 float
列表一起使用,因为这两种类型都支持所需的运算符:
> turn (1,2) (3,3) (2,1);;
val it : Direction = Right
> turn (1.0,2.0) (3.0,3.0) (2.0,1.0);;
val it : Direction = Right
虽然不如Haskell那么优雅,但可以在紧要关头使用。但在实践中,我并不太想念它。在我的职业生涯中,我很少错过 generic arithmetic;我发现它说明了一些商业上最成功的编程语言(Java、C#)不支持泛型算术。
I was trying to put both int and float into a list construction:
> let l2=[1;2.0];;
let l2=[1;2.0];;
----------^^^
stdin(50,11): error FS0001: This expression was expected to have type
int
but here has type
float
>
好吧,如果我在 Haskell 中写入 let l3=[1,2.0],它可以构建包含所有 "fractional" 元素的列表。为什么 F# 不支持自动类型推断? F#是基于.net的,Int和Float都是Object类型吧?为什么 Int 和 Float 不能放在一个列表中,而有些元素会自动进行类型提升或转换?似乎 F# 的类型系统使得编写更通用的程序变得非常困难。
有什么提示吗?
F# 没有像 Haskell 这样的类型类。虽然它基于 .NET,但 int
和 float
之间唯一的共同类型是 obj
,并且 obj
不提供使您能够执行算术的运算符。
不过,您可以定义一个包含int
和float
值的列表:
let l : obj list = [1; 2.0]
不过,这接近于 无用,因为除非您尝试向下转换它们,否则您无法使用这些值做任何事情。它也是 不安全的 ,因为这样的列表很容易包含非数字值:
let l : obj list = [1; 2.0; "foo"]
然而,一切并没有丢失,因为您可以使用 inline
关键字让编译器找出各种输入所需的运算符:
type Direction = Left = -1 | Straight = 0 | Right = 1
let inline turn (x1, y1) (x2, y2) (x3, y3) =
let prod = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)
if prod > LanguagePrimitives.GenericZero then Direction.Left
elif prod < LanguagePrimitives.GenericZero then Direction.Right
else Direction.Straight
此函数具有以下类型:
val inline turn :
x1: ^a * y1: ^f -> x2: ^b * y2: ^g -> x3: ^j * y3: ^e -> Direction
when ( ^b or ^a) : (static member ( - ) : ^b * ^a -> ^c) and
( ^j or ^a) : (static member ( - ) : ^j * ^a -> ^i) and
( ^c or ^d) : (static member ( * ) : ^c * ^d -> ^l) and
( ^e or ^f) : (static member ( - ) : ^e * ^f -> ^d) and
( ^g or ^f) : (static member ( - ) : ^g * ^f -> ^h) and
( ^h or ^i) : (static member ( * ) : ^h * ^i -> ^k) and
( ^l or ^k) : (static member ( - ) : ^l * ^k -> ^m) and
^m : (static member get_Zero : -> ^m) and ^m : comparison
它声明 ^a
和 ^b
是支持静态运算符 -
的类型,当使用时,returns 类型 ^c
,支持静态运算符*
等。
这个例子来自my convex hull example.
它可以与 int
和 float
列表一起使用,因为这两种类型都支持所需的运算符:
> turn (1,2) (3,3) (2,1);;
val it : Direction = Right
> turn (1.0,2.0) (3.0,3.0) (2.0,1.0);;
val it : Direction = Right
虽然不如Haskell那么优雅,但可以在紧要关头使用。但在实践中,我并不太想念它。在我的职业生涯中,我很少错过 generic arithmetic;我发现它说明了一些商业上最成功的编程语言(Java、C#)不支持泛型算术。