Reputation: 95
I'm pretty new to F#, I'm trying to write some code that does not discriminate between floats and integers, and treats them both merely as "numbers". To start off with I want a simple "add" function that will add two numbers, where each number can be an integer or a float.
This is my current approach which doesn't feel at all elegant, probably due to my ignorance of the intricacies of F#
type Number =
| Integer of int
| Float of float
let add (a: Number, b: Number) : Number =
match a with
| Integer(a) ->
match b with
| Integer(b) -> Integer(a + b)
| Float(b) -> Float(double a + b)
| Float(a) ->
match b with
| Integer(b) -> Float(a + double b)
| Float(b) -> Float(a + b)
This works, but it's a lot of code for such a simple function. In particular, if I wanted also a "subtract" function, I would have to copy/paste the entire function and just change the "+"s to "-"s, which seems really inefficient. Is there a nicer way to do this in F#?
Upvotes: 1
Views: 215
Reputation: 1721
Applying the inline
keyword will cause F# to use Statically-Resolved Type Parameters (SRTP) to automatically generate two functions (one for int and one for float) at compile time.
let inline add a b = a + b
add 5 6
add 5.2 5.8
The type of this function is as shown below. Note that each parameter, as well as the return value, could have a different type (^a, ^b, or ^c). The ^
symbol indicates an SRTP.
val inline add :
a: ^a -> b: ^b -> ^c
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
You could, if you wish, force both the parameters and the return value to all have the same type.
let inline add (a: ^a) (b: ^a) : ^a = a + b
This function's signature is:
val inline add :
a: ^a -> b: ^a -> ^a when ^a : (static member ( + ) : ^a * ^a -> ^a)
Upvotes: 2
Reputation: 11577
One possible approach could be this
// Generic function to reduce two numbers into a number
// i reduces nested integers
// f reduces nested floats
// In order for i,f to be merged into single reducer
// F# would have to support higher ranked types (I think)
// but F# does not
let reduce i f (a: Number) (b: Number) : Number =
match a, b with
| Integer a, Integer b -> Integer (i a b)
| Integer a, Float b -> Float (f (float a) b)
| Float a, Integer b -> Float (f a (float b))
| Float a, Float b -> Float (f a b)
// Define common reducer functions
// Operators +,-,* and / are parametric polymorphic
// So the first ( + ) becomes an int -> int -> int
// the second ( + ) becomes an float -> float -> float
let add = reduce ( + ) ( + )
let subtract = reduce ( - ) ( - )
let multiply = reduce ( * ) ( * )
let divide = reduce ( / ) ( / )
Upvotes: 2