如何对通用计算函数使用 f# 约束?

How to use f# constraints for generic calculation functions?

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. 如何表达限制,例如"only signed number"?
  3. 有没有办法在 F# 中扩展类型约束系统?

提前致谢。

这使其通用:

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

但无法将其限制为带符号的原始类型。

已经回答过的第一个问题的解决方案是将内联函数与 GenericZero 一起使用,仅此而已。

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

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 的模块中,你可以知道你可以达到什么水平玩转这些限制。