F#, Chain Of Responsibility, And High Ordered Functions
November 19, 2013 1 Comment
I was working though Tao Liu’s F# for C# Developers book
when I got to the chapter on F# and Design Patterns. This is a great chapter. Typically F# books introduce the language from the language on out. This chapter takes a different approach – it shows the language in action using commonly accepted design patterns. I was working through the section on the Chain Of Responsibility pattern when I got to this code snippet
- let check f (record,result) =
- if not result then record, false
- else record, f(record)
Looking at the code, I get that we are assigning a function called check that has a tuple with record called ‘record” and a Boolean called ‘result’. However, what is that ‘f’? Basically, what the F is that f in F#? Looking at the REPL results, I see that it was this signature:
Basically, f is a function called ‘f’ that I am passing in. I am then invoking this function here:
- let chainOfResponsibility = check validAge >> check validHeight >> check validWeight
- let test1 = {Name="Test"; Age=45; Weight=175.; Height=175.}
- printfn "test result = %A" (chainOfResponsibility (test1, true) |> snd)
So this is an example, I think, of two language constructs. The first is high ordered functions – where check takes in a function as a parameter. Next, this is an example of currying – where chainOfResponsibility only passes in the function, not the tuple. I think.
To further research this, I looked up Chain of Responsibility on Wikipedia and took a look at the C# example. To do it the C# way, created 6 files
with each file having the implementation found on the Wikipedia article. The Logger class defines the behavior:
- public abstract class Logger
- {
- protected LogLevel logMask;
- protected Logger next;
- public Logger(LogLevel mask)
- {
- this.logMask = mask;
- }
- public Logger SetNext(Logger nextlogger)
- {
- next = nextlogger;
- return nextlogger;
- }
- public void Message(string msg, LogLevel severity)
- {
- if ((severity & logMask) != 0)
- {
- WriteMessage(msg);
- }
- if (next != null)
- {
- next.Message(msg, severity);
- }
- }
- abstract public void WriteMessage(string msg);
- }
and the individual implementations in the classes:
- public class FileLogger : Logger
- {
- public FileLogger(LogLevel mask)
- : base(mask)
- { }
- public override void WriteMessage(string msg)
- {
- //Placeholder for File writing logic
- Debug.WriteLine("Writing to Log File: " + msg);
- }
- }
What is interesting to me is the Logger.SetNext() method is used to move to the next implementation in the chain. I then set to write the example in FSharp using Liu’s template. I did this:
- type Logger() =
- let LogToConsole logLevel =
- //Implementation
- true
- let LogToEmail logLevel =
- //Implementation
- true
- let LogToFile logLevel =
- //Implementation
- true
- let check f (logLevel, result) =
- if not result then logLevel, false
- else logLevel, f(logLevel)
- let chainOfResponsibility =
- check LogToConsole >> check LogToEmail >> check LogToFile
- let mutable logLevel = LogLevel.None
- member this.CurrentLogLevel with get() = logLevel
- and set(v) = logLevel <- v
- member this.WriteMessage () =
- chainOfResponsibility (logLevel,true) |> snd
So there are a couple of observations.
1) F# is more terse – and more readable
2) Instead of creating a MoveNext function, the chainOfResponsibility uses the >> operator to move to the next.
3) These 2 code bases are not functionally equivalent – I have to keep working on the F# one.
4) How much fun is F#?
Pingback: F# Weekly #47, 2013 | Sergey Tihon's Blog