English 中文(简体)
具有泛型参数类型的函数
原标题:
  • 时间:2009-02-01 16:09:47
  •  标签:

我正在尝试找出如何定义一个能够作用于多种类型的参数(例如,int 和 int64)的函数。据我所知,在 F# 中无法进行函数重载(当然编译器会报错)。比如下面的函数:

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

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

由于我对语言还比较陌生,我可能正在尝试做一些不可能的事情,请让我知道是否有其他解决方案。

最佳回答

过载通常是类型推断语言的心头大患(至少在像 F# 这样的类型系统不足以包含类型类时)。在 F# 中,您有许多选择。

  • Use overloading on methods (members of a type), in which case overloading works much like as in other .Net languages (you can ad-hoc overload members, provided calls can be distinguished by the number/type of parameters)
  • Use "inline", "^", and static member constraints for ad-hoc overloading on functions (this is what most of the various math operators that need to work on int/float/etc.; the syntax here is weird, this is little-used apart from the F# library)
  • Simulate type classes by passing an extra dictionary-of-operations parameter (this is what INumeric does in one of the F# PowerPack libraries to generalize various Math algorithms for arbitrary user-defined types)
  • Fall back to dynamic typing (pass in an obj parameter, do a dynamic type test, throw a runtime exception for bad type)

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

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
问题回答

这有效:

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)

更多关于此事请见此处

可以做到。请查看这个hubFS帖子

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

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

注意:没有编译时类型检查。例如,sqrt_int "blabla"可以编译通过,但在运行时会出现FormatException异常。

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

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

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

| :? type ->

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

| :? type as foo ->




相关问题
热门标签