3

Let's say I am solving a particular problem and come up with a function

let function parameter1 ... = 
     a lot of adding, multiplying & so on with a lot of 
     literals all over the place

Now this function works fine if my parameters are of type int. But somewhere I will need it to go up to 11, I will need that extra push into int64 or even BigInteger. So what do I do? I copy&paste the function, change the name, and go hunting for all literal appearances that make the compiler think the function should operate on int. And this sucks.

Is there a way to do this:

let greatFunction param1 param2 = (param1+1)/(param2*2)

where param1 and param2 can be any combo of integer types?

Edit:

Expanding a bit on a great tip by kvb below, I came up with the following

module NumericLiteralG 

  let inline FromZero() = LanguagePrimitives.GenericZero
  let inline FromOne() = LanguagePrimitives.GenericOne
  let inline FromInt32 n =
    let rec loop nIn nOut = 
        if nIn>0 then loop (nIn - 1) (nOut + LanguagePrimitives.GenericOne)
        else nOut
    loop n LanguagePrimitives.GenericZero

so it becomes a bit less ugly when used

let inline halfSquare num =
   let res = num / 2G
   res * res

let solve1 = halfSquare 5I 
let solve2 = halfSquare 5.0
let solve3 = halfSquare 5uy

Now the question is should I use this? If not, why not?

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
CxDoo
  • 121
  • 1
  • 9
  • I think in general you would need a `Numeric` class/interface for that, which afaik doesn't exist in .net (yet?). But maybe some special F# stuff can help you. Hava a look at [this question](http://stackoverflow.com/q/2225949/1346276). – phipsgabler Jul 19 '12 at 13:45
  • Why do you ask if you should use this? Inlined functions using `GenericXXX` are the definitive way to do this in F#. – Daniel Jul 19 '12 at 18:14
  • Hi Daniel. I am new to F# and I am **really** trying, meaning I strive to do it the functional way even though the solution screaming in my head is all for loops and side effects and gotos and whatnots. Now, I am **very** ignorant about how this above works under the hood, coming from Digital Electronics/Microcontroller background. I used to know the timing of every instruction in my code. I used to hand-optimize my compiler output. So it's a honest question directed to people who understand the subject much better than me. FWIW I could be doing the stupidest thing and not be aware of it. – CxDoo Jul 19 '12 at 18:52
  • Coming from that background, I would say this works comparably to C++ templates. Notice the ubiquity of `inline`. – Daniel Jul 19 '12 at 18:57
  • And here is a reason why I am asking: http://stackoverflow.com/questions/2840714/f-static-member-type-constraints This guy obviously copied my hard work and posted it here **2 years ago**. My Google Fu is weak. – CxDoo Jul 19 '12 at 19:14

4 Answers4

5

I think Generic Arithmetic is common problem in .NET languages. There are many articles explaining different approaches and very soon I will post another one explaining mine which is similar to the solution you posted.

Now, if you ask me if should you use it, I would say: as long as you understand what you are doing why not? I'm using it partially in production and have no issues at all, but because I care about run-time performance I use overloading to resolve everything at compile time. Then to speed up compile time I redefine basic math operators to operate in the same type, otherwise type signatures get really complicated and may take ages to compile.

There are more things to consider but for your specific problem here is a sample code:

open System.Numerics

type FromInt = FromInt with
    static member ($) (FromInt, _:sbyte     ) = fun (x:int) -> sbyte      x
    static member ($) (FromInt, _:int16     ) = fun (x:int) -> int16      x
    static member ($) (FromInt, _:int32     ) = id
    static member ($) (FromInt, _:float     ) = fun (x:int) -> float      x
    static member ($) (FromInt, _:float32   ) = fun (x:int) -> float32    x
    static member ($) (FromInt, _:int64     ) = fun (x:int) -> int64      x
    static member ($) (FromInt, _:nativeint ) = fun (x:int) -> nativeint  x
    static member ($) (FromInt, _:byte      ) = fun (x:int) -> byte       x
    static member ($) (FromInt, _:uint16    ) = fun (x:int) -> uint16     x
    static member ($) (FromInt, _:char      ) = fun (x:int) -> char       x
    static member ($) (FromInt, _:uint32    ) = fun (x:int) -> uint32     x
    static member ($) (FromInt, _:uint64    ) = fun (x:int) -> uint64     x
    static member ($) (FromInt, _:unativeint) = fun (x:int) -> unativeint x
    static member ($) (FromInt, _:bigint    ) = fun (x:int) -> bigint     x
    static member ($) (FromInt, _:decimal   ) = fun (x:int) -> decimal    x
    static member ($) (FromInt, _:Complex   ) = fun (x:int) -> Complex(float x,0.0)  

let inline fromInt (a:int) : ^t = (FromInt  $  Unchecked.defaultof< ^t>) a

module NumericLiteralG =
    let inline FromZero() =LanguagePrimitives.GenericZero
    let inline FromOne() = LanguagePrimitives.GenericOne
    let inline FromInt32 (i:int)     = fromInt i


// This will reduce the number of types inferred, will reduce compile time too.
let inline (+) (a:^t) (b:^t) : ^t = a + b
let inline (-) (a:^t) (b:^t) : ^t = a - b
let inline (*) (a:^t) (b:^t) : ^t = a * b
let inline (/) (a:^t) (b:^t) : ^t = a / b
let inline (~-) (a:^t) : ^t = -a


let inline halfSquare num =
   let res = num / 2G
   res * res

let solve1 = halfSquare 5I 
let solve2 = halfSquare 5.0
let solve3 = halfSquare 5uy 

// Define more generic math functions.
Gus
  • 25,839
  • 2
  • 51
  • 76
3

Here's an article on generic, numeric calculations in F#. In general, you have two options:

  • Static member constraints
  • Global numeric associations (available in F# PowerPack)

...or you can combine these techniques.

In your case, it sounds like static constraints will work.

A simple example from that article:

let inline halfSquare num =
   let res = LanguagePrimitives.DivideByInt num 2
   res * res
Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
  • That is a very interesting article. Not really focused on integer->integer stuff which makes me wonder is this what I am asking for really that unusual in F#? Or is it trivial to do and everyone knows it and I'm the only one asking for it on the internets? – CxDoo Jul 19 '12 at 14:23
3

One way to do it is combining inline keyword and generic bits from LanguagePrimitives module:

let inline greatFunction param1 param2 = 
    let one = LanguagePrimitives.GenericOne
    let two = one + one
    (param1+one)/(param2*two)

// Usage
let f1 = greatFunction 4 2
let f2 = greatFunction 4L 2L
let f3 = greatFunction 4I 2I
pad
  • 41,040
  • 7
  • 92
  • 166
  • Meanwhile, after reading the above article I came up with this: let inline genericN n = let rec loop nIn nOut = if nIn<>GenericZero then loop (nIn - GenericOne) (nOut + GenericOne) else nOut loop n GenericZero let inline halfSquare num = let res = num / (genericN 2) res * res The question now is how ineffective and ugly is this? Or that's it? BTW I give up on editing this answer, the code will stay inline cause that's the way it is. – CxDoo Jul 19 '12 at 14:25
  • Yes, it is ugly and inefficient. Maybe these are reasons why you don't see it very often. – pad Jul 19 '12 at 15:07
0

While not ideal, and kind of bypassing your main question, you can add type annotations to force the compiler's hand:

let greatFunction (param1:int64) (param2:int64) : int64 = (param1+1)/(param2*2)

Now of course there are no implicit conversions in F#, so you will need to add L to all numeric literals, but they will show up as compiler errors at least.

Guvante
  • 18,775
  • 1
  • 33
  • 64