skip to main | skip to sidebar
Showing posts with label F#. Show all posts
Showing posts with label F#. Show all posts

Friday, February 19, 2010

F# examples talk at BFG

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#.

  1. 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 pressing ALT-Enter. The F# Interactive transcript went something like:
    val uncurry : ('a -> 'b -> 'c) -> 'a * 'b -> 'c

    > 1 + 2;;
    val it : int = 3

    > uncurry (+) (1,2);;
    val it : int = 3

    > (+) 1 2;;
    val it : int = 3
    This lead to a discussion on operators and infix/prefix notation. The post F# Option orElse getOrElse functions should cover that.

  2. Next was the |> operator, which has type signature 'a -> ('a -> 'b) -> 'b.
    // Haskell catMaybes
    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 [,]
    We briefly compared |> with the Haskell $ function, which has the type signature (a -> b) -> a -> b.

  3. 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

  4. 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;}

    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.Height
    The F# overload-o-phone covers our discussion on arithmetic operators. The inline keyword is mentioned in the comments.

  5. 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.
Along the way there were a few comments about F# being strictly evaluated and having no effect tracking system, no higher-order parametric polymorphism and no type classes.

Thanks to everyone that participated in the discussion and I hope the talk was interesting.

Thursday, September 24, 2009

FsCheck xUnit integration

I am using xUnit.net at work on an F# project. I wanted to incorporate FsCheck and check properties via xUnit.net, but there is no built-in integration. I tried this from Matthew Podwysocki, but it is out of date with respect to the versions I am using (xUnit.net 1.5, FsCheck 0.6.1).

EDIT 28/09/2009: Here is the new and improved version based on Kurt's comment.

module FsCheckXunit

open FsCheck
open FsCheck.Runner
open Xunit

let xUnitRunner =
{ new IRunner with
member x.OnArguments(_,_,_) = ()
member x.OnShrink(_,_) = ()
member x.OnFinished(name, result) =
match result with
| True data -> Assert.True(true)
| _ -> Assert.True(false, testFinishedToString name result)
}

let config = {quick with Runner = xUnitRunner}

Below is the first version I adapted from Matthew's post and the FsCheck source code.
module FsCheckXunit

open FsCheck
open FsCheck.Runner
open Xunit

let xUnitRunner =
{ new IRunner with
member x.OnArguments(_,_,_) = ()
member x.OnShrink(_,_) = ()
member x.OnFinished(name, result) =
match result with
| True data ->
Assert.True(true)
data.Stamps |> Seq.iter (fun x -> printfn "%d - %A" (fst x) (snd x))
| False (data,_,args,Exception e,_) ->
Assert.True(false, sprintf "%s - Falsifiable after %i tests (%i shrinks): %A with exception %O"
name data.NumberOfTests data.NumberOfShrinks args e)
| False (data,_,args,Timeout i,_) ->
Assert.True(false, sprintf "%s - Timeout of %i milliseconds exceeded after %i tests (%i shrinks): %A"
name i data.NumberOfTests data.NumberOfShrinks args)
| False (data,_,args,_,_) ->
Assert.True(false, sprintf "%s - Falsifiable after %i tests (%i shrinks): %A"
name data.NumberOfTests data.NumberOfShrinks args)
| Exhausted data -> Assert.True(false, sprintf "Exhausted after %d tests" (data.NumberOfTests) )
}

let config = {quick with Runner = xUnitRunner}

Thursday, July 30, 2009

F# Option orElse getOrElse functions

Consider the following function that takes a Sequence of (pairs) int * int.

let f s =
let o = Seq.tryFind (fun (_, x) -> x < 0) s
if Option.isSome o then
Option.get o |> fst
else
let p = Seq.tryFind (fun (_, x) -> x = 0) s
if Option.isSome p then Option.get p |> fst
else 10
Essentially I am looking for the first pair in the sequence where the second element in the pair satisfies a particular condition and then returning the corresponding first element or a default (i.e. 10) if no satisfactory pair was found.

That code is pretty ordinary. First of all, I would like to improve on the two calls to Seq.tryFind.
let f s =
let tf g = Seq.tryFind (fun (_,x) -> g x) s
let o = tf (fun x -> x < 0)
if Option.isSome o then
Option.get o |> fst
else
let p = tf ((=) 0)
if Option.isSome p then Option.get p |> fst
else 10
Now if the Scala Option orElse and getOrElse functions were available as well as the Haskell backquotes infix syntax then we could really make a difference.

orElse : 'T option -> 'T option -> 'T option
let orElse o p = if Option.isSome o then o else p

getOrElse : 'T option -> 'T -> 'T
let getOrElse o d = match o with | Some x -> x | _ -> d

// N.B. this is not valid F#

let f s =
let tf g = Seq.tryFind (fun (_,x) -> g x) s
tf (fun x -> x < 0) `orElse` tf ((=) 0) |> Option.map fst `getOrElse` 10
Neither orElse or getOrElse exist in the F# Option module. However the Core function defaultArg is essentially getOrElse.

Unfortunately F# doesn't have a way to use a (non-operator) function in infix form, like the backquotes in Haskell. However, we can define operators, which are essentially just infix functions.

let (|?) = orElse

let (|?|) = defaultArg
Now we can write a valid F# version.

let f s =
let tf g = Seq.tryFind (fun (_,x) -> g x) s
tf (fun x -> x < 0) |? tf ((=) 0) |> Option.map fst |?| 10

Laziness and Composability
Notice that in the original example, the second tryFind is only executed if the first one is unsuccessful because the then expression of an if statement is lazy, i.e. it is only evaluated if the condition is false.

However functions in F# are strict by default i.e. their arguments are evaluated prior to application. Consequently both tryFinds are evaluated, irrespective of their values, as they are arguments to orElse.

So here is an example where laziness is required to achieve reasonable composability. Implementing this gives the final code.

let orElse o (p:'a option Lazy) = if Option.isSome o then o else p.Force()

let (|?) = orElse

let (|?|) = defaultArg

let f s =
let tf g = Seq.tryFind (fun (_,x) -> g x) s
tf (fun x -> x < 0) |? lazy (tf ((=) 0)) |> Option.map fst |?| 10

Saturday, June 6, 2009

F# Interactive on Mac OS X

Installing F# on Mac OS X is easy (with MacPorts):

$ sudo port install fsharp

I have version 1.9.4.19 installed. Microsoft released a newer version several weeks ago, however it doesn't seem to have made it into MacPorts yet. Then to start F# Interactive:
$ fsi --readline --no-gui

There is basic history with the up/down arrows, but the usual emacs key bindings are not available, so entering and editing text is very slow and painful. I tried to fix this with rlwrap.
$ sudo port install rlwrap

As fsi is already using readline, rlwrap needs the -a option, See the man page for details, but on the mac you are required to provide a string (representing a password prompt the wrapped application might show) argument for this option. It is completely unnecessary for me at the moment, so I just picked something obvious as a placeholder.
$ rlwrap -aPassword: fsi --readline --no-gui

F# Interactive starts up with the usual splash text, but there seems to be some control characters automatically input and the console clears to something like this:
- 0;3R0;3R;3R;3R;3R;3R;3R

The emacs key bindings work, so this text can be deleted with C-a C-k and terminating the now empty line as per normal in fsi with ;; brings up the usual fsi prompt.

Unfortunately, if you type in an invalid expression eg. x = 3;;, the cursor is placed in the middle of the error message. When pressing up arrow to go back through the history, sometimes the prompt is replaced by partial text from a viewed history line.

So this is a pretty dodgy solution. If anyone knows how to get it to work properly, please leave a comment.

Subscribe to: Comments (Atom)
 

AltStyle によって変換されたページ (->オリジナル) /