I’ve been writing about F# for a little while now, and how it has influenced the way I code in C# on an everyday basis for 3 years. During the last couple weeks, I’ve finally had the chance to use F# at work for a real project (I’ll probably talk about that later). Yesterday evening (Europe time) at Build, Roslyn was open-sourced. That’s nice and people are probably going to talk about it for a while. But for me there was another very nice announcement : Visual F# is now taking contributions! F# is already an exciting language, full of clever features. With this new step, I’m convinced that it will go even further and I’m already looking forward to F# 4.0!
So don’t expect to see many more posts about C# on this blog, I’ve totally gone F#.
And now, the latest sample from Arolla’s Code Jam. The aim was to write code to express values with a range of uncertainty, and operations which manipulate those numbers. For instance, a value 3.5 +/- 0.5, multiplied by 1.0 +/- 0.1. The goal of the kata was to write code that expresses intent (the why), and not implementation details (the how).
In F#, I’ve just defined a type to wrap both the value and its precision, and an operator to conveniently build those values :
type Number = | Number of float * float let (+-) n a = Number (n, a)
This code alone already allows me to build values just by writing expressions such as “4.0 +- 1.0”. The next step for me was to write some expressive tests, using FsUnit.
let [<test>] ``A number with accuracy is equal to itself`` () = (1.0 +- 0.5) |> should equal (1.0 +- 0.5) let [<test>] ``Sum of two numbers sums the numbers and accuracies`` () = ((5.0 +- 0.5) + (3.0 +- 0.5)) |> should equal (8.0 +- 1.0)
Although we could handle distinctly every operator with specific logic, the goal of this kata is to express the intent in a generic fashion, so here is a combine method that has no specific meaning of what an operator does:
let combine op (Number(n1, a1), Number(n2, a2)) = let all = seq { for x in [n1-a1; n1+a1] do for y in [n2-a2; n2+a2] do yield op x y } let minimum = all |> Seq.min let maximum = all |> Seq.max Number((minimum + maximum) / 2.0, (maximum - minimum) / 2.0)
Finally, we just have to add operator overloading to the type, the full code becomes:
type Number = | Number of float * float with override x.ToString() = match x with | Number(a, b) -> sprintf "%f +- %f" a b static member private combine op (Number(n1, a1), Number(n2, a2)) = let all = seq { for x in [n1-a1; n1+a1] do for y in [n2-a2; n2+a2] do yield op x y } let minimum = all |> Seq.min let maximum = all |> Seq.max Number((minimum + maximum) / 2.0, (maximum - minimum) / 2.0) static member (+) (x, y) = (x, y) |> Number.combine (+) static member (-) (x, y) = (x, y) |> Number.combine (-) static member (*) (x, y) = (x, y) |> Number.combine (*) static member (/) (x, y) = (x, y) |> Number.combine (/) static member Pow (x, y) = (x, y) |> Number.combine ( ** )
Thanks to @luketopia for the interactions on Twitter, and the trick which allows to pass the operator ** as an argument: use spaces between the parenthesis and the operator!