多种类型的F#功能类型注释

时间:2017-05-04 14:50:55

标签: generics f# functional-programming

我试图定义一个"准通用"功能。我不希望它完全是通用的,而是希望它只适用于"整数"类型(即byte,sbyte,int16,uint16,int,uint32,int64,uint64,bigint)。

如何将它放入函数定义的类型注释中?为了澄清,我将如何重写以下代码以实际工作(仅使用3种类型,大概不会失去泛化):

let square (x: int|int64|bigint) =
    x * x

2 个答案:

答案 0 :(得分:6)

首先,在运行时无法使用标准.NET泛型来解决此类型约束。

F#允许您通过在编译时解析它们并插入正确的内联函数来表达有限形式的此类约束。这使用statically resolved type parameters

对于你所描述的案例来说,这很简单,你可以写:

let inline square x = x * x

这适用于定义了'T运算符的任何类型*

您还可以显式应用特定的静态/成员约束,但这需要更复杂的语法,例如

let inline id item =
    ( ^T : (member Id : int) (item))

此示例函数将对公开类型为Id的{​​{1}}属性的任何类型进行操作。

更新:根据您所描述的特定用例,您确实是类型类。那些在F#中并不存在(除了一些硬编码的例子),但您可以使用标记类型和成员约束来模拟它们,这是一个例子:

int

请注意,这允许您指定要允许其使用功能的确切类型。

答案 1 :(得分:2)

您的问题基本上有3种方法

a)您使用的类型已经支持您要应用于它们的运算符/方法
在这种情况下,只需在您的函数前添加inline即可,

b)您可以完全控制所使用的类型
也就是说,您可以在函数定义中定义新成员,而无需使用扩展方法。在这种情况下,您可以在每个实现所需内容的类上定义一个方法

type MyInt16 = MyInt16 of int
    with
    static member Mult(x, y) = 
        match x,y with
        | MyInt16 x', MyInt16 y' -> MyInt16 (x' * y')

type MyInt32 = MyInt32 of int
    with
    static member Mult(x, y) = 
        match x,y with
        | MyInt32 x', MyInt32 y' -> MyInt32 (x' * y')

和一个使用泛型类型约束的内联函数,使用这种相当奇怪的语法

let inline mult (x:^T) (y:^T) = (^T : (static member Mult: ^T -> ^T -> ^T) (x, y))

然后你测试

let a = MyInt16 2
let b = MyInt16 3

let c = mult a b

这很有效。让我们看看当我们使用不同类型时会发生什么

let d = mult a (MyInt32 3)

以上内容会出错。

c)您无法完全控制您的类型
也就是说,您无法在类型中定义方法,但您必须使用扩展方法。扩展方法的问题在于它们无法使用 内联函数使用泛型类型约束。在这种情况下,您最好回退到parameter approach I described here

type MultParam =
    | MyInt16Param of System.Int16
    | MyInt32Param of System.Int32
with 
    static member op_Implicit(x: System.Int16) = MyInt16Param x
    static member op_Implicit(x: System.Int32) = MyInt32Param x

然后再次定义一个内联函数,该函数具有通用约束,可将传入类型转换为包装类型

let inline (!>) (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x)

并添加您的实施。这次有点啰嗦,因为我们需要使用模式匹配

let inline mult' (x: ^T) (y: ^T) : ^T =
    let x' = !> x
    let y' = !> y 
    let r = 
        match x', y' with
        | MyInt16Param x'', MyInt16Param y'' -> x'' * y'' |> box
        | MyInt32Param x'', MyInt32Param y'' -> x'' * y'' |> box
        | _ -> failwith "Not possible"
    r :?> _

现在让我们再次测试

let e = mult' (int16(2)) (int16(3))

这很有效。让我们看看当我们使用不同类型时会发生什么

let f = mult' (int16(2)) (int32(3))

应该再次出现错误。

选项b)基本上是Haskells类型类特征的仿真,其中as 选项c)更接近OCamls多态变体