8

Probably a silly question, but I just got started with F# and I've got a little problem.

Say I have a function like this:

let multiplyByTwo x = x * 2

When I call this like this:

let result = multiplyByTwo 5

Everything is alright, the result is 10.

When I call it like this:

let result = multiplyByTwo 2.5

I expect to get 5 or 5.0 as a result. The actual result however is this:

let result = multiplyByTwo 2.5;;
---------------------------------^^^

stdin(4,28): error FS0001: This expression was expected to have type

int     

but here has type

float

Because I want this function to be somewhat generic (i.e. accept both floating point numbers and integers), I don't like this. My question of course: how does one solve this?

Leon Cullens
  • 12,276
  • 10
  • 51
  • 85
  • I don't know much about F# hence the comment but I presume the '2' in your function is being regarded as an int & is expecting x to be an int also, is there some way you can cast? – Nigel B May 30 '12 at 22:01
  • The 'incompatibility' of `float` and `int` used to really bug me when I started with F#, but after a couple of years, I've slowly come round to the usefulness of considering them as different types. Oftentimes it has forced me to ask myself what I really *want*. – Benjol May 31 '12 at 06:21
  • I believe this is a language flaw and my workaround was to use floats for everything when I want to deal with real numbers and don't care about performance or accuracy. – Velizar Hristov Mar 06 '16 at 11:54

3 Answers3

14

When you write a numeric literal in F# (such as 2 or 3.14), the compiler treats that as a value of a specific type and so code that uses numeric literals will not be polymorphic. You can either convert input to a single type and work with that type (like float in desco's answer) or use more advanced features of F#...

Certain numeric operations can be written in a polymorphic way, if you mark the code as inline (this way, the compiler can represent additional constraints and statically resolve them) and if you only use polymorphic primitives (with additional static constraints).

Standard operators are polymorpic in inline functions and the F# library provides a way to get polymorphic value representing 1 and 0 (though not 2), but that's enough to write the function you wanted:

let inline twoTimes n = 
  let one = LanguagePrimitives.GenericOne
  n * (one + one)

twoTimes 2
twoTimes 2.0

If you want to make this nicer, you can define a numeric literal (see Daniel's answer to earlier StackOverflow question) and then you can actually write just:

let inline twoTimes n = n * 2G

The special numeric literal 2G is translated to a call to a function of NumericLiteralG which sums specified number of generic 1 values using the technique I used above (so it won't be efficient for large numbers!) For more information, you see also my recent article on writing generic numeric code in F#.

Community
  • 1
  • 1
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • I accepted desco's answer because it's the easiest (and imho cleanest) solution, but I thank you for you great post! Btw, I read on your website that you are one of Don Syme's students, nice! – Leon Cullens May 30 '12 at 22:25
  • Nice. I Figured the`"answer" would be something todowih getting rid of the literal, didn't have scooby doo how`to do it though. – Tony Hopkinson May 30 '12 at 22:27
  • @LeonCullens If you're happy with the result being `float`, then the answer by @desco is the best option. Don Syme is actually still my (second) PhD supervisor :-). – Tomas Petricek May 30 '12 at 22:30
  • Yep, I don't really care about the results of the function, as long as I can assign any numeric parameter to it. – Leon Cullens May 30 '12 at 22:39
12
let inline mulBy2 x = (float x) * 2.0

let a = mulBy2 3 // 6.0 : float
let b = mulBy2 2.5 // 5.0 : float
let c = mulBy2 "4" // 8.0 : float
desco
  • 16,642
  • 1
  • 45
  • 56
  • I find this method to be the cleanest way. Looks like a good solution. I just don't fully understand the 'inline' keyword yet (it appears that it parses the input, but I dont know when to use and when not to use it), but I'll look into that on Google. – Leon Cullens May 30 '12 at 22:18
  • 1
    From MSDN: "Without the inline modifier, type inference forces the function to take a specific type, in this case int. But with the inline modifier, the function is also inferred to have a statically resolved type parameter. This means that the function accepts any type that supports a conversion to float." – Aleš Roubíček May 31 '12 at 05:58
-1

If you aren't afraid using "little hacks", this might be useful:

// Copied from Core.LanguagePrimitives.IntrinsicFunctions.retype
[<NoDynamicInvocation>]
let inline retype (x:'a) : 'b = (# "" x : 'b #)

let inline multiplyByTwo (x:'a) = x * (retype 2:'a)

// use
let result1 = multiplyByTwo 5 // 10
let result2 = multiplyByTwo 2.5 // 5.0

This construct is not type safe since type checking is done in runtime. Also, quotations are relatively slow.

Be Brave Be Like Ukraine
  • 7,596
  • 3
  • 42
  • 66