Monthly Archives: May 2010

Records with Functions and Parameters

In my last post, we saw how you could use records in a way which was object-like in nature. In this post, I want to expand on that post, showing how to use functions as members which take parameters. As this is more complicated, I will use a simpler record type which only has one member. The code is:

open System.IO
open System.Globalization

type DateTimeStringPainter =
    { ShowString: System.DateTime -> System.String; }


let culture = CultureInfo.CurrentCulture
let flag = "F"

let strFunctionGiveFormatter fmt dtFunctionFormatString =
    {ShowString = (fun dtStruct -> dtFunctionFormatString fmt dtStruct);}

let fnCreateFunctionToPrintDTString fmtter formatFlag =
    strFunctionGiveFormatter fmtter (fun formatProvider dateTimeStruct -> dateTimeStruct.ToString(formatFlag, formatProvider))
    
let createRecordDateTimeStringPainter = fnCreateFunctionToPrintDTString culture flag

printfn "%s" (createRecordDateTimeStringPainter.ShowString(System.DateTime.Now))

//printfn "%s" (System.DateTime.Now.ToString("F", culture))

printfn "\nPress any key to kill ..."
System.Console.ReadKey() |> ignore 

The code above creates a function which will output a date to the console. Note that this could be done with 1 line of code (see line 20). But be cool – it’s demonstration code!

The custom type, DateTimeStringPainter, requires the implementation of a method which takes a DateTime value as a parameter and gives a string result (line 5).

In line 11, I have defined the implementation of the ShowString function. It takes the DateTime parameter dtStruct. Notice on the right hand side, there are 3 items dtFunctionFormatString, fmt and dtStruct. As you will see, that is a function, dtFunctionFormatString, which takes 2 parameters fmt and dtStruct. That function gives us the string we require to meet the implementation requirements of ShowString.

Lines 13 – 14 show the rest of the story. On line 14, strFunctionGiveFormatter is called, passing to it a formatter and a function. Recall from line 10 that strFunctionGiveFormatter takes variables of those two types. The function is declared “in-line” and passed to strFunctionGiveFormatter on line 14.

Going back to line 11 quickly, dtFunctionFormatString takes two parameters. It gets its DateTime parementer from the left hand side of ShowString (dtStruct), and it gets its other parameter from the incoming parameter fmt. And that is the core thing to understand in this example.

Line 16 shows an identifier being bound to the fnCreateFunctionToPrintDTString function, with an IFormatter object and a format flag passed in as parameters. And line 18 sees ShowString actually being called, with a DateTime instance being passed in (i.e. which goes to the left hand side of ShowString on line 11).

And that is how you pass around functions with parameters to a record which has a function as one of its members.

Using Records like Objects (F#)

Records are an F# thing. And the concept which I will set out in the example below took me a while to get my head around (the syntax). But it’s really very simple.

Records can have functions as their fields. The following example involves the use of a custom type (NamerRecord). NamerRecord is a record with 2 functions as its fields.

  1. First off, I will create/initialise a NamerRecord record with a name (line 19). At that point, neither of the record’s functions have been called.
  2. Then, I’ll call the record’s SayName function (line 22).
  3. Then I will change the name of the record (line 23),
  4. Following which, I will call the SayName function again (line 24).
open System.Drawing

// a NamerRecord record that will act as our object
type NamerRecord =
    { Rename: string -> unit;
      SayName: unit -> unit }

//  This identifier (aNameThing) has the type "string -> NamerRecord". 
//  We pass in a string and end up with a NamerRecord.
//  It's a functional programming thing. Functions can be values. 
let aNameThing initialName =    
    let currentName = ref initialName
    {   Rename = 
            fun newName -> currentName := newName;
        SayName = 
            fun() -> printf "%s\n" !currentName; }

//  This is the instantiation of our NamerRecord type
let dave = aNameThing "Bobby Sixkiller"

//  call the functions defined in the NamerRecord type
dave.SayName()
dave.Rename("Vince Black (aka Reno Raines)")
dave.SayName()

printf "\n\n"
System.Console.ReadKey() |> ignore

We’re getting object-like behaviour out of a record:

In my next post, I’ll take this a bit further to confuse you even more!