Earlier this week I gave a short talk at the Brisbane Functional Group on F#. The idea was to give an introductory feel for the language by way of some simple examples. The talk was intended to be short so I assumed knowledge of previously discussed idioms such as tuples and curried type signatures.
I took the examples (with permission :-) from the environmental sensing project I work on at MQUTeR. This post is intended as a reference for those who attended rather than a self-contained introduction to F#.
- The
uncurry
function was first up.let uncurry f (x,y) = f x y
We talked about the syntax of function definition, tuples and type inference in Visual Studio. We inserted the function into F# Interactive (the REPL available in Visual Studio) by highlighting it and pressingALT-Enter
. The F# Interactive transcript went something like:val uncurry : ('a -> 'b -> 'c) -> 'a * 'b -> 'c
This lead to a discussion on operators and infix/prefix notation. The post F# Option orElse getOrElse functions should cover that.
> 1 + 2;;
val it : int = 3
> uncurry (+) (1,2);;
val it : int = 3
> (+) 1 2;;
val it : int = 3 - Next was the
|>
operator, which has type signature'a -> ('a -> 'b) -> 'b
.// Haskell catMaybes
We briefly compared
let catOptions xs = Seq.filter Option.isSome xs |> Seq.map Option.get
let euclidianDist (x1, y1) (x2, y2) = (x1 - x2) ** 2.0 + (y1 - y2) ** 2.0 |> sqrt
let spt' t a =
let m = Math.Matrix.ofArray2D a
allPeaks t m |> removeSmall |> dilate t m |> Math.Matrix.toArray2D
// type signatures
// allPeaks : float -> matrix -> matrix
// removeSmall : matrix -> matrix
// dilate : float -> matrix -> matrix -> matrix
// spt' : float -> float [,] -> float [,]|>
with the Haskell$
function, which has the type signature(a -> b) -> a -> b
. - A quick look at type annotations, pattern matching and Haskell like guards. The example is an implementation of the MATLAB smooth function, which calculates a moving average. We didn't dwell on the entire function, just the relevant parts.
(*
yy = smooth(y,span) sets the span of the moving average to span. span must be odd.
If span = 5 then the first few elements of yy are given by:
yy(1) = y(1)
yy(2) = (y(1) + y(2) + y(3))/3
yy(3) = (y(1) + y(2) + y(3) + y(4) + y(5))/5
yy(4) = (y(2) + y(3) + y(4) + y(5) + y(6))/5
...
*)
let smooth s (a:float []) =
let n = (s - 1) / 2
let f i _ =
let (b, l) = match i with
| _ when i < n -> (0, i*2+1)
| _ when i + n < a.GetLength(0) -> (i-n, s)
| _ -> (i-(a.GetLength(0)-1-i), (a.GetLength(0)-1-i)*2+1)
Array.sum (Array.sub a b l) / (float l) // TODO try Array.average here
Array.mapi f a - Records are a common kind of data type. The labels are not automatically available as functions like they are in Haskell.
type 'a Rectangle = {Left:'a; Top:'a; Right:'a; Bottom:'a; Width:'a; Height:'a;}
The F# overload-o-phone covers our discussion on arithmetic operators. The
let inline cornersToRect l r t b = {Left=l; Top=t; Right=r; Bottom=b; Width=r-l; Height=t-b;}
let inline lengthsToRect l t w h = {Left=l; Top=t; Right=l+w-1; Bottom=t+h-1; Width=w; Height=h;}
let fcornersToRect (l:float) r t b = cornersToRect l r t b // for C#
let left r = r.Left
let right r = r.Right
let top r = r.Top
let bottom r = r.Bottom
let bottomLeft r = (r.Left, r.Bottom)
let width r = r.Width
let height r = r.Height
let inline area r = r.Width * r.Heightinline
keyword is mentioned in the comments. - Finally we looked at available API documentation. I find that I generally skim API documentation by first looking at function type signatures. The F# PowerPack is a useful library developed by the F# team and the format of the documentation supports my approach.
The F# API documentation was previously in this format. It has now been moved to MSDN and the list of function type signatures is not presented for each module, rendering it almost useless.
Thanks to everyone that participated in the discussion and I hope the talk was interesting.