我试图弄清楚如何定义一个适用于多种类型参数的函数(例如int和int64)。据我所知,F#中无法实现函数重载(当然编译器会抱怨)。以下面的函数为例。
let sqrt_int = function
| n:int -> int (sqrt (float n))
| n:int64 -> int64 (sqrt (float n))
编译器当然抱怨语法无效(似乎不支持模式匹配中的类型约束),尽管我认为这说明了我想要实现的内容:一个对多个参数类型进行操作并返回值的函数相应的类型。我觉得在F#中使用泛型类型/类型推断/模式匹配的某种组合是可能的,但语法已经躲过了我。我也尝试过使用:?运算符(动态类型测试)和当子句在模式匹配块中时,但这仍会产生所有排序错误。
由于我对语言不熟悉,我可能会尝试在这里做一些不可能的事情,所以如果有其他解决方案,请告诉我。
答案 0 :(得分:61)
重载通常是类型推断语言的bugaboo(至少在类似F#的情况下,类型系统不足以包含类型类)。你在F#中有很多选择:
对于您的特定示例,我可能只使用方法重载:
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
答案 1 :(得分:17)
这有效:
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的更多信息。
答案 2 :(得分:14)
是的,这可以做到。看看this hubFS thread。
在这种情况下,解决方案是:
let inline retype (x:'a) : 'b = (# "" x : 'b #)
let inline sqrt_int (n:'a) = retype (sqrt (float n)) : 'a
警告:没有编译时类型检查。即sqrt_int "blabla"
编译得很好,但你会在运行时得到一个FormatException。
答案 3 :(得分:9)
这是使用运行时类型检查的另一种方式......
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
答案 4 :(得分:2)
不要忽略已经提供的正确答案,但事实上你可以在模式匹配中使用类型约束。语法是:
| :? type ->
或者如果你想结合类型检查和转换:
| :? type as foo ->