48

我试图弄清楚如何定义一个适用于多种类型参数(例如 int 和 int64)的函数。据我了解,在 F# 中函数重载是不可能的(当然编译器会抱怨)。以下面的函数为例。

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

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

由于我对这门语言相当陌生,我很可能在这里尝试做一些不可能的事情,所以如果有替代解决方案,请告诉我。

4

5 回答 5

64

重载通常是类型推断语言的错误(至少当像 F# 一样,类型系统不足以包含类型类时)。在 F# 中有许多选择:

  • 对方法(类型的成员)使用重载,在这种情况下,重载的工作方式与其他 .Net 语言非常相似(您可以临时重载成员,前提是调用可以通过参数的数量/类型来区分)
  • 使用“内联”、“^”和静态成员约束对函数进行临时重载(这是大多数需要处理 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
于 2009-02-01T18:50:49.253 回答
20

这有效:

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)

更多关于这里的信息。

于 2013-09-28T20:40:19.313 回答
15

是的,这是可以做到的。看看这个 hubFS 线程

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

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

警告:没有编译时类型检查。即sqrt_int "blabla"编译得很好,但你会在运行时得到一个 FormatException。

于 2009-02-01T16:42:26.627 回答
10

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

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
于 2009-02-01T21:21:16.187 回答
3

不要放弃已经提供的正确答案,但实际上您可以在模式匹配中使用类型约束。语法是:

| :? type ->

或者,如果您想结合类型检查和强制转换:

| :? type as foo ->
于 2009-02-01T19:10:01.960 回答