Unlikely1879
Unlikely1879

Reputation: 95

Simple Add function to work with integers and floats

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

Answers (2)

Scott Hutchinson
Scott Hutchinson

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

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

Related Questions