Function Templates in F #

Let's say I solve a specific 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 need it to get to 11, I need an extra push to int64 or even BigInteger. So what should I do? I copy and paste the function, change the name and go hunting for all the literal occurrences that make the compiler think that the function should work on int. And that sucks.

Is there any way to do this:

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

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

Edit:

By deploying the bit at the big end using 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 gets a little 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?

+2
source share
4 answers

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

Now, if you ask me whether to use it, I would say: as long as you understand what you are doing, why not? I use it partly in production and have no problems at all, but since I really like runtime performance, I use overloading to resolve everything at compile time. Then, to speed up the compilation time, I redefine the basic mathematical operators to work in the same type, otherwise the types of signatures become very complex and can take an age 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. 
+4
source

Here's an article about general, numerical calculations in F # . In general, you have two options:

  • Static Member Limitations
  • Global number associations (available in F # PowerPack)

... or you can combine these methods.

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

A simple example from this article:

 let inline halfSquare num = let res = LanguagePrimitives.DivideByInt num 2 res * res 
+3
source

One way to do this is to combine the inline keyword and common bits from LanguagePrimitives :

 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 
+3
source

Until you idealize and get around your main question, you can add type annotations to force the compiler command:

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

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

0
source

All Articles