如何将f#约束用于通用计算函数?

时间:2015-02-20 14:35:24

标签: f# inline type-constraints

type Point<'t> =
    val X : 't
    val Y : 't
    new(x : 't,y : 't) = { X = x; Y = y }

let clampedSubtract (p1:Point<_>) (p2:Point<_>) =
    Point( max (p2.X - p1.X) 0, max (p2.Y - p1.Y) 0 )

如果你看一下上面的代码,你会发现,该函数并没有像它应该的那样通用。

首先,在max表达式中使用0会将类型钳制为int。但它应该是Point<'t>具有的类型,而不是Point<int>

但更重要的是,如果将签名类型用于`t。

,则此函数只能按预期工作

这提出了我的一些问题:

  1. 有没有办法从通用(数字)类型中获取中性元素(零)?
  2. 如何表达“仅签名号码”等限制?
  3. 有没有办法在F#中扩展类型约束系统?
  4. 提前谢谢。

2 个答案:

答案 0 :(得分:5)

这使它成为通用的:

let inline clampedSubtract (p1:Point<_>) (p2:Point<_>) =
    let zero = LanguagePrimitives.GenericZero
    Point( max (p2.X - p1.X) zero, max (p2.Y - p1.Y) zero )

但是没有办法将它约束为已签名的原始类型。

答案 1 :(得分:5)

已经回答的第一个问题的解决方案是使用内联函数和GenericZero以及所有这些。

关于签名限制,实际上有一种简单的方法可以将其限制为签名类型。在某处使用仅为签名类型定义的通用一元否定:

let inline clampedSubtract (p1:Point<_>) (p2:Point<_>) =
    let zero = LanguagePrimitives.GenericZero
    Point( max (p2.X + -p1.X) zero, max (p2.Y + -p1.Y) zero )

let result1 = clampedSubtract (Point(4  , 5  )) (Point(4  , 5  ))
let result2 = clampedSubtract (Point(4y , 5y )) (Point(4y , 5y ))
let result3 = clampedSubtract (Point(4uy, 5uy)) (Point(4uy, 5uy)) // doesn't compile

通常,如果要将任何泛型函数限制为有符号类型,可以定义此函数:

let inline whenSigned x = ignore (-x)

let inline clampedSubtract (p1:Point<_>) (p2:Point<_>) =
    whenSigned p1.X
    let zero = LanguagePrimitives.GenericZero
    Point( max (p2.X - p1.X) zero, max (p2.Y - p1.Y) zero )

最后关于你的第三个问题,我不太清楚扩展类型系统是什么意思。您可以自己创建静态约束,从这个意义上说,系统已经是可扩展的。

前段时间我做了一个项目来模拟一些Haskell类型,该项目代码的一部分仍然在FsControl的模块中,你可以知道你可以用这些约束达到什么级别。< / p>