简体   繁体   English

具有通用参数类型的函数

[英]Functions with generic parameter types

I am trying to figure out how to define a function that works on multiple types of parameters (eg int and int64). 我试图弄清楚如何定义一个适用于多种类型参数的函数(例如int和int64)。 As I understand it, function overloading is not possible in F# (certainly the compiler complains). 据我所知,F#中无法实现函数重载(当然编译器会抱怨)。 Take for example the following function. 以下面的功能为例。

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

The compiler of course complains that the syntax is invalid (type constraints in pattern matching are not supported it seems), though I think this illustrates what I would like to achieve: a function that operates on several parameter types and returns a value of the according type. 编译器当然抱怨语法无效(似乎不支持模式匹配中的类型约束),尽管我认为这说明了我想要实现的内容:一个对多个参数类型进行操作并返回相应值的函数类型。 I have a feeling that this is possible in F# using some combination of generic types/type inference/pattern matching, but the syntax has eluded me. 我觉得在F#中使用泛型类型/类型推断/模式匹配的某种组合是可能的,但语法已经躲过了我。 I've also tried using the :? 我也尝试过使用:? operator (dynamic type tests) and when clauses in the pattern matching block, but this still produces all sorts errors. 在图案匹配块运算符(动态型测试), 条款,但这仍然会产生各种错误。

As I am rather new to the language, I may very well be trying to do something impossible here, so please let me know if there is alternative solution. 由于我对这门语言不熟悉,我很可能会尝试在这里做一些不可能的事情,所以如果有其他解决方案,请告诉我。

Overloading is typically the bugaboo of type-inferenced languages (at least when, like F#, the type system isn't powerful enough to contain type-classes). 重载通常是类型推断语言的问题(至少在类似F#的情况下,类型系统不足以包含类型类)。 There are a number of choices you have in 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) 在方法(类型成员)上使用重载,在这种情况下,重载的工作方式与其他.Net语言非常相似(您可以使用ad-hoc重载成员,提供的调用可以通过参数的数量/类型来区分)
  • 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) 使用“inline”,“^”和静态成员约束来对函数进行ad-hoc重载(这是大多数需要处理int / float / etc的各种数学运算符;这里的语法很奇怪,这是除了F#库之外很少使用
  • 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) 通过传递额外的操作字典参数来模拟类型类(这是INumeric在其中一个F#PowerPack库中执行的操作,用于为任意用户定义的类型推广各种数学算法)
  • Fall back to dynamic typing (pass in an 'obj' parameter, do a dynamic type test, throw a runtime exception for bad type) 回退到动态类型(传入'obj'参数,进行动态类型测试,为坏类型抛出运行时异常)

For your particular example, I would probably just use method overloading: 对于您的特定示例,我可能只使用方法重载:

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

This works: 这有效:

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

It uses static constraints and overloading, which makes a compile-time lookup on the type of the argument. 它使用静态约束和重载,这使得对参数类型进行编译时查找。

The static constraints are automatically generated in presence of an operator (operator $ in this case) but it can always be written by hand: 静态约束是在运算符(在这种情况下为运算符$ )的情况下自动生成的,但它总是可以手写:

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)

More about this here . 更多关于这里

Yes, this can be done. 是的,这可以做到。 Take a look at this hubFS thread . 看看这个hubFS线程

In this case, the solution would be: 在这种情况下,解决方案是:

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

Caveat : no compile-time type checking. 警告 :没有编译时类型检查。 Ie sqrt_int "blabla" compiles fine but you'll get a FormatException at runtime. sqrt_int "blabla"编译得很好,但你会在运行时得到一个FormatException。

Here's another way using runtime type checks... 这是使用运行时类型检查的另一种方式......

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

Not to take away from the correct answers already provided, but you can in fact use type constraints in pattern matching. 不要忽略已经提供的正确答案,但事实上你可以在模式匹配中使用类型约束。 The syntax is: 语法是:

| :? type ->

Or if you want to combine type checking and casting: 或者,如果要组合类型检查和转换:

| :? type as foo ->

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM