首页 文章

具有通用参数类型的函数

提问于
浏览
45

我试图弄清楚如何定义一个适用于多种类型参数的函数(例如int和int64) . 据我所知,F#中无法实现函数重载(当然编译器会抱怨) . 以下面的功能为例 .

let sqrt_int = function
    | n:int   -> int (sqrt (float n))
    | n:int64 -> int64 (sqrt (float n))

编译器当然抱怨语法无效(似乎不支持模式匹配中的类型约束),尽管我认为这说明了我想要实现的内容:一个对多个参数类型进行操作并返回相应值的函数类型 . 我觉得在F#中使用泛型类型/类型推断/模式匹配的某种组合是可能的,但语法却让我望而却步 . 我也尝试过使用:?运算符(动态类型测试)和模式匹配块中的子句,但这仍会产生所有排序错误 .

由于我对语言不熟悉,我可能会尝试在这里做一些不可能的事情,所以如果有替代解决方案,请告诉我 .

5 回答

  • 16

    不要忽略已经提供的正确答案,但事实上你可以在模式匹配中使用类型约束 . 语法是:

    | :? type ->
    

    或者,如果要组合类型检查和转换:

    | :? type as foo ->
    
  • 2

    这是使用运行时类型检查的另一种方式......

    let sqrt_int<'a> (x:'a) : 'a = // '
        match box x with
        | :? int as i -> downcast (i |> float |> sqrt |> int |> box)
        | :? int64 as i -> downcast (i |> float |> sqrt |> int64 |> box)
        | _ -> failwith "boo"
    
    let a = sqrt_int 9
    let b = sqrt_int 100L
    let c = sqrt_int "foo" // boom
    
  • 59

    重载通常是类型推断语言的问题(至少在类似F#的情况下,类型系统不足以包含类型类) . 你在F#中有很多选择:

    • 在方法(类型成员)上使用重载,在这种情况下,重载的工作方式与在其他.Net语言中的工作方式非常相似(您可以使用ad-hoc重载成员,提供的调用可以通过参数的数量/类型来区分)

    • 使用"inline","^"和静态成员约束对函数进行ad-hoc重载(这是大多数需要处理int / float / etc的各种数学运算符;这里的语法很奇怪,这个很少使用除了F#库)

    • 通过传递额外的字典操作参数来模拟类型类(这是INumeric在其中一个F#PowerPack库中为任意用户定义类型推广各种数学算法的内容)

    • 回退到动态类型(传入'obj'参数,执行动态类型测试,为坏类型抛出运行时异常)

    对于您的特定示例,我可能只使用方法重载:

    type MathOps =
        static member sqrt_int(x:int) = x |> float |> sqrt |> int
        static member sqrt_int(x:int64) = x |> float |> sqrt |> int64
    
    let x = MathOps.sqrt_int 9
    let y = MathOps.sqrt_int 100L
    
  • 9

    这有效:

    type T = T with
        static member ($) (T, n:int  ) = int   (sqrt (float n))
        static member ($) (T, n:int64) = int64 (sqrt (float n))
    
    let inline sqrt_int (x:'t) :'t = T $ x
    

    它使用静态约束和重载,这使得对参数类型进行编译时查找 .

    静态约束是在运算符(本例中为运算符 $ )的情况下自动生成的,但始终可以手动编写:

    type T = T with
        static member Sqr (T, n:int  ) = int   (sqrt (float n))
        static member Sqr (T, n:int64) = int64 (sqrt (float n))
    
    let inline sqrt_int (x:'N) :'N = ((^T or ^N) : (static member Sqr: ^T * ^N -> _) T, x)
    

    更多关于这个here .

  • 14

    是的,这可以做到 . 看看this hubFS thread .

    在这种情况下,解决方案将是:

    let inline retype (x:'a) : 'b = (# "" x : 'b #)
    let inline sqrt_int (n:'a) = retype (sqrt (float n)) : 'a
    

    Caveat :没有编译时类型检查 . 即 sqrt_int "blabla" 编译正常但你会在运行时得到一个FormatException .

相关问题