In this case, the easiest solution is probably to just inline your function:
let inline sqr x = x * x
val inline sqr : ^a -> ^b when ^a : (static member ( * ) : ^a * ^a -> ^b

Now, you can use it with both ints and floats.

By on 4/9/2008 9:22 PM ()

What would be the best way of doing something like this?

1
let rec fib n a b = let ab = a+b in if n<ab then b else fib n b ab;;

I don't think you can use inline with let rec, but I'd potentially want to be able to use this function with int32s, int64s and bigints.

By on 4/14/2008 2:06 PM ()

You could just wrap the recursive definition in an inline function:

1
2
3
4
5
let inline fibs n a b =
     let rec loop n a b =
         let ab = a + b
         if n < ab then b else loop n b ab
     loop n a b

I'm not sure, though, whether this is the way the language designers intended inline to be used.

Stephan

By on 4/14/2008 2:34 PM ()

Thanks Stephan, that does the job.

I'm not sure I like the way that the type of the + operator defaults to int when it's overloaded for other types. Is this something that will be fixed in future revisions of the compiler or is it quite a hard thing for type inference to cope with?

I'm really enjoying playing about with F# though. It feels like a very practical, fast, typed Scheme.

By on 4/17/2008 12:55 PM ()

As I understand it, the problem isn't so much type inference, as the nature of the .NET platform (CodeDOM and CLR==Common Language Runtime) that is being compiled to: an int and a float are two different value types; currently (unless you "inline"), the F# compiler has to pick a single target type -- so it picks whichever type you use it with first.

Don has mentioned the eventual possibility that it would act like a C++ template in this case: generate a version of the function for each type it is used with. I.E., automatically doing for you the equivalent of gneverov's suggestion behind the scenes; when "fib" is referenced, the version matching the parameter types would be called.

An alternative would be to target the DLR (Dynamic Language Runtime), like IronPython, but I believe the code would run more slowly, so I like the direction F# is moving in.

~TMSteve

By on 4/17/2008 4:18 PM ()

If for some reason one doesn't wish to inline, as Stephan and gneverov showed, here is a somewhat slower approach. This shows how to use GetNumericAssociation, and redefine the arithmetic '+' operator to use that association.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#light
open Microsoft.FSharp.Math.GlobalAssociations

// Slow way to do generic math operations.
module GenericMath =
  let (+) (n1:'a) (n2:'a) =
    let num = GetNumericAssociation<'a>()
    num.Add(n1,n2)
  let add3 (n1:'a) (n2:'a) (n3:'a) =
    (n1 + n2) + n3

  let test() =
    // "add3" is generic!
    let i1 = add3 1 2 3
    let f1 = add3 1.0 2.0 3.0

    let pam msg a = printfn "%s = %A" msg a
    i1 |> pam "i1"
    f1 |> pam "f1"

GenericMath.test()
printf "----- Done: Press any key. -----"
System.Console.ReadKey(false) |> ignore
By on 4/18/2008 6:00 PM ()

I'm dubious anyone would want to use the following form, but perhaps it is a useful example of some F# syntax.

This is clumsier to use, but I'm hoping slightly faster than the previous version, as the GetNumericAssociation is only done once, rather than on every add.

If you really care about speed though, you will probably just "inline" your function instead, as that would compile directly to fast bytecodes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#light
open Microsoft.FSharp.Math.GlobalAssociations

// templated math operations,
// with infix operators inside the templated class.
module TemplatedNumeric =

  type MMath<'b> =
    abstract add2: 'b -> 'b -> 'b
    abstract add3: 'b -> 'b -> 'b -> 'b
  
  // Templated math class.
  let M<'c>() =
    let num :Math.INumeric<'c> = GetNumericAssociation<'c>()
    // Override '+' to only work with type 'c.
    let (+) n1 n2 = num.Add(n1,n2)
    // '+' operator can be used within following member functions.
    { new MMath<'c> with
      member x.add2 (n1:'c) (n2:'c) :'c = n1 + n2
      member x.add3 (n1:'c) (n2:'c) (n3:'c) :'c = (n1 + n2) + n3
    }
  
  let test() =
    // Define a type for each value type.
    let I = M<int>()
    let F = M<float>()
    // Use the templated types.
    let i1 = I.add3 1 2 3
    let f1 = F.add3 1.0 2.0 3.0

    let pam msg a = printfn "%s = %A" msg a
    i1 |> pam "i1"
    f1 |> pam "f1"
    ()

TemplatedNumeric.test()
printf "----- Done: Press any key. -----"
System.Console.ReadKey(false) |> ignore
By on 4/18/2008 6:06 PM ()

I think I reasonable solution is to use Stephan's inline function but do not call it directly from your code but instead use it to create regular functions for each type you need the function overloaded on. E.g.,

1
2
3
let fibsInt32 n a b : int32 = fibs n a b
let fibsInt64 n a b : int64 = fibs n a b
let fibsBigint n a b : bigint = fibs n a b
By on 4/14/2008 9:20 PM ()

Thanks a lot for the reply.

It appears a bit counter intuitive to me that although operator * is overloaded, F# requires type of its arguments to be determined at compile time (in non inline functions). If for some reason I don't want to use inline functions, are there other solutions?

By on 4/10/2008 6:10 AM ()

Hi,

There's another solution with GetNumericAssociation. You can find more details here:

[link:cs.hubfs.net] [broken]
[link:cs.hubfs.net]

Laurent.

By on 4/10/2008 7:00 AM ()

That was exactly the same question as mine. Thanks very much.

By on 4/10/2008 7:35 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper