Functions with generic parameter types


Answers

Yes, this can be done. Take a look at this hubFS thread.

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. I.e. sqrt_int "blabla" compiles fine but you'll get a FormatException at runtime.

Question

I am trying to figure out how to define a function that works on multiple types of parameters (e.g. int and int64). As I understand it, function overloading is not possible in F# (certainly the compiler complains). 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. 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.




See my answer to this question:

Functions with generic parameter types

Briefly:

  • You can overload members of a class (but not let-bound functions)
  • You can use 'inline' and 'hat' types
  • You can simulate Haskell type classes and explicitly pass dictionaries-of-methods
  • You can use do run-time type tests casting from 'obj'



Does F# have generic arithmetic support?

As brian mentioned, there is some built-in support for generic arithmethic and you can use 'static constraints' which allow you to define some generic functions yourself (although this is a bit limited).

In addition to this, you can also use dynamic 'numeric associations', which is a bit slower when using in a function, but it can be nicely used for example to define your own vector or matrix type. Here is an example:

#r "FSharp.PowerPack.dll"
open Microsoft.FSharp.Math

let twoTimesLarger (n:'a) (m:'a) = 
  let ops = GlobalAssociations.GetNumericAssociation<'a>()
  let sel = if ops.Compare(n, m) > 0 then n else m
  ops.Multiply(sel, ops.Add(ops.One, ops.One))

We first need to reference F# PowerPack library which contains the functionality. Then we define a generic function with a signature 'a -> 'a -> 'a. The first line dynamically gets numeric operations for working with the type 'a (it essentially uses some lookup table with the type as the key). Then you can use methods of the numeric operations object to perform things like multiplication, addition (Multiply, Add) and many others. The function works with any numbers:

twoTimesLarger 3 4  
twoTimesLarger 2.3 2.4
twoTimesLarger "a" "b" // Throws an exception!

When you define your own numeric type, you can define its numeric operations and register them using GlobalAssociations.RegisterNumericAssociation. I believe this also means that you'll be able to use built-in F# Matrix<YourType> and Vector<YourType> after registering the operations.




You could do something like this.

let inline sum<'a when 'a : (static member (+) : 'a -> 'a -> 'a)> a b =
    a + b

let result = sum<int> 3 4

However if I try let result = sum 3 4 I get the error "type ambiguity inherent in the use of the operator '( + )'"






Tags