为什么F#没有列表元素的类型推断?

时间:2016-02-19 02:58:03

标签: list f# inline inference

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中写l3 = [1,2.0],它可以构建列表以包含所有“小数”元素。为什么F#不支持自动类型推断? F#基于.net,Int和Float都是Object类型,对吧?为什么Int和Float无法放入一个列表中,而某些元素会自动进行类型提升或转换?似乎F#的类型系统使编写更多通用程序变得非常困难。

任何提示?

1 个答案:

答案 0 :(得分:11)

F#没有类似Haskell的类型类。虽然它基于.NET,但intfloat之间唯一的共同类型是obj,而obj没有提供使您能够执行算术的运算符。

仍然,您可以定义包含intfloat值的列表:

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是支持静态运算符-的类型,在使用时返回类型^c,它支持静态运算符{ {1}},等等。

此示例取自my convex hull example

它可以与*int列表一起使用,因为这两种类型都支持所需的运算符:

float

这并不像Haskell那么优雅,但它可以在紧要关头使用。但在实践中,我不会错过很多。在我的职业生涯中,我很少想念泛型算术;我发现它说一些商业上最成功的编程语言(Java,C#)不支持通用算法。