KaizenConf Wrapup - Approaching Functional Programming
This past weekend, I had the opportunity to attend and give a workshop at the Continuous Improvement in Software Development, or KaizenConf. I'm grateful to Scott Bellware and Dave Laribee for organizing this event. The tone and subject matter at this conference was both refreshing and interesting. This conference had a focus on continuous improvement through methodology, communication, as well as technology, which was a bit different than the ALT.NET Conferences, which had much more of a technology tilt. The conversations had not only in the sessions, but also the hallways in between were enlightening, and it was enjoyable to get so many perspectives on software development issues.
Approaching Functional Programming
I want to thank everyone who attended my workshop on Thursday on functional programming. It's a topic that I'm passionate about in regards to how it can improve your code significantly. The title of my workshop was "Functional Programming - Is It A Game Changer", and instead, I think the talk is better titled "Approaching Functional Programming". In this talk, I not only talked about the basics of functional programming, but how to mix and match the object oriented and imperative styles as well. I had a pretty good attendance, even though I was up against a Domain Driven Design talk by Mr. Laribee and Textual DSLs in Boo with Ayende. This was one of the sessions that was not recorded, but instead, I hope to give you some basic ideas of what I covered. This talk was also given as part of an open space discussion as well.
Some of the topics I covered were:
- What is Functional Programming?
- Why is it important?
- How can I do it in .NET? With C#? With F#?
- How does it affect your code?
- Moving from imperative to functional
Many times when we're faced with functional programming, many people look with a blank stare on several topics, including immutability, functional composition, currying and most of all, monads. Let's just go through some simple examples of each and learn how they might help us.
From Mutable to Immutable
One of the easiest examples to understand is moving from mutable data structures to mutable ones. The advantage of doing so is especially apparent when moving to data parallel applications. If you don't have to worry about data ownership and which thread is writing to the structure, the less opportunity for unintended consequences. Let's take a simple point class as our first example:
{
public int x;
public int y;
public Point2D(int x, int y)
{
this.x = x;
this.y = y;
}
public void MoveBy(int dx, int dy)
{
x += dx;
y += dy;
}
}
Classes such as the above scream to me to be a value object, in the Domain Driven Design parlance. As identity is not important to us, if we need to change it, we through it away and get a new one. This stresses that these value objects are immutable. Let's move this class to be an immutable data structure instead.
{
public readonly int x;
public readonly int y;
public Point2D(int x, int y)
this.x = x;
this.y = y;
}
public Point2D MoveBy(int dx, int dy)
return new Point2D(x + dx, y + dy);
}
}
As you can see, there is no unnecessary mutation here, instead, when we want to move the point, we are given a new point class. This isn't to say that given a standard object oriented approach, that all values must be immutable, but instead, the state must be explicit. I like the approach that F# takes in which you have to go out of your way to make your mutability explicit through either the mutable keyword or a reference cell.
val mutable x:float
val mutable y:float
new(dx, dy) = { x = dx; y = dy; }
member this.MoveBy(dx, dy) =
this.x <- this.x + dx
this.y <- this.y + dy
Projects such as Spec#, which is being productized in .NET 4.0 through Code Contracts, could also help. More on this in a subsequent post. Moving on to functional composition over inheritance.
Functional Composition over Inheritance
Many times in our .NET code, we find ourselves with large amounts of pomp and circumstance code which does a simple operation such as sorting. Instead, we could apply functional composition through delegates to solve this issue. Take the example of the following code used to sort some employee data.
type LevelComparer() =
interface IComparer<Employee> with
member x.Compare(e1, e2) =
e1.Level.CompareTo(e2.Level)
type NameComparer() =
interface IComparer<Employee> with
member x.Compare(e1, e2) =
e1.Name.CompareTo(e2.Name)
let employeeData = new ResizeArray<Employee>()
employeeData.Sort(new LevelComparer())
employeeData.Sort(new NameComparer())
As you may notice, we have two classes that do one operation, sorting. You may also note that the sort method also takes an overload with a Comparer<T>. This allows us to use lambda expressions instead and get rid of the unnecessary class noise. Let's replace that code to use functional composition to remove some of the code.
employeeData.Sort(fun e1 e2 -> e1.Level.CompareTo(e2.Level))
employeeData.Sort(fun e1 e2 -> e1.Name.CompareTo(e2.Name))
By using the application of functions, we are able to make our code more concise. Let's turn to some more examples. Let's talk about functional composition in terms of applying curried functions.
Currying
The topic of currying is another hot topic when understanding functional programming. To be able to reduce the number of arguments from a given function from two down to one, we're able to make very reusable functions. Some of these examples were transcribed from Practical Groovy and applied to F#, so they may look familiar. Let's take a simple example of a little rules engine to calculate a book price. Not only will we be covering currying, but also being able to compose functions together in a meaningful way.
author:string;
price:float;
category:string
}
let bk = { name = "Expert F#"; author = "Don Syme"; price = 55.99; category = "CompSci" }
// Constants
let discountRate = 0.1
let taxRate = 0.55
// Calculation functions
let calcDiscountedPrice = (*) (1. - discountRate)
let calcTax = (*) (1. + taxRate)
let calcNetPrice = calcTax << calcDiscountedPrice
// Calculate net prices
let netPrice = calcNetPrice(bk.price)
This above code allows me to specify my functions for calcuating discounted prices, tax and net price as simple, reusable functions. As you can see, the calcDiscountedPrice and calcTax functions are missing their final argument, which is a float. Each of these functions follow the signature float => float. The calcNetPrice function may look odd to you, but I'm using what is called a combinator, which allows me to compose functions f and g such as this: f(g x). After I apply the two functions together, I can then supply the final argument, the book price, to calculate the net price. I emphasized over and over, small reusable functions and then compose them together is the way to make functional programming powerful.
Let's look at one last one example here. In this example, we're creating a simple library application to loan books. We want the ability to display loaned books and books available for loan. First, you want to create small reusable functions to determine whether to display a book that is loaned or not. Then we want a way to iterate these books.
Title:string; Author:string; CatalogNumber:string; mutable OnLoan:bool
}
with override x.ToString() =
sprintf "Title: %s; Author: %s" x.Title x.Author
type Library(name:string) =
let stock = new Dictionary<_,_>()
let displayLoanedBook =
fun bk -> if bk.OnLoan = true then printfn "%O" bk
let displayAvailableBook =
fun bk -> if bk.OnLoan = false then printfn "%O" bk
let displayLoanedBooks =
Seq.iter displayLoanedBook
let displayAvailableBooks =
Seq.iter displayAvailableBook
member x.AddBook(title, author, catalogNumber) =
let bk = { Title = title; Author = author; CatalogNumber = catalogNumber; OnLoan = false }
stock.[catalogNumber] <- bk
member x.LendBook(catalogNumber) =
stock.[catalogNumber].OnLoan <- true
member x.DisplayBooksOnLoan() =
printfn "Library %s" name
printfn "Books on loan"
displayLoanedBooks(stock.Values)
member x.DisplayBooksAvailableForLoan() =
printfn "Library %s" name
printfn "Books available for loan"
displayAvailableBooks(stock.Values)
What's interesting about the above code is what I created a function to determine whether to display a loaned book, or an available book. Then I created a curried function which applies Seq.iter to the given filter function so that we print out the proper items. Finally, with the DisplayBooksAvailableForLoad and DisplayBooksOnLoan, we apply the final argument, the collection of books to each function to display the data. Using techniques such as this, we're able to compose these functions together in pretty interesting and powerful ways. Languages such as F#, which make functions true first class citizens, techniques such as this have relatively no friction to do this.
Monads
Lastly, monads were actually the most fun to discuss. Because they are often misunderstood, not in how they are constructed, but why you might use them. In a previous post, I went through a few monads such as the identity, maybe, list and async monad, but let's cover one last one here, the error monad. Why you might use them is the ability to execute the given code, and either get back the value of the computation, or an exception with detailed information about the failure. This makes state management a bit easier on how to handle in a uniform way. Let's define a simple builder for this.
match d with
| Choice2_1 a -> f a
| Choice2_2 exn -> Choice2_2(exn)
let resultM v = Choice2_1(v)
let delayM f = f()
type ErrorBuilder() =
member x.Bind(v, d) = bindE v d
member x.Return(v) = resultM v
member x.Delay(f) = delayM f
let error = new ErrorBuilder()
let tryCatchPrim f =
try
Choice2_1(f())
with
| err -> Choice2_2(err)
Now that the basic binding is complete, let's understand what we just did. The bind function takes the given input, and determines whether its an error or the real value. If the real value, then invoke the function with the given argument of a, which returns a Choice<'a, exn> which says it has either the real value or the exception. Once the basic functions are defined, we can create the builder, and finally, create a wrapper which allows us to extend our given code through this try/catch block. Let's add one method to the System.IO.File class so that we can return a Choice<'a, exn>.
static member TryReadAllText(path) =
tryCatchPrim (fun () -> System.IO.File.ReadAllText(path))
This makes wrapping existing functions rather easy in order to use this monad. Let's do a simple example of how we might use it.
return text
}
let error_print (eValue:Choice<'a, exn>) =
match eValue with
| Choice2_1 a -> a.ToString()
| Choice2_2 exn -> exn.Message
printfn "%s" (error_print e)
Brian McNamara of the F# team has a great example on the continuation monad which is interesting as well and easy to understand. So, once again, why you might use monads is to handle state issues, such as asynchronous programming, continuations, exception handling and so on. The list can go on, but they're really important to understand.
Wrapping It Up
I also had the opportunity to attend some other talks such as Using and Abusing ASP.NET MVC for Fun and Profit, Presentation Patterns, All About MEF and a few others. The workshops were really well put together and I think many people got a lot out of them, because I know I sure did. If you have a chance, some of the videos are available, so I'd highly recommend you check them out at your leisure. The open spaces sessions were also great for sharing ideas, especially the ones around lean software development. It was great to have Tom and Mary Poppendieck there at some of the sessions to discuss these issues we face, in our continuous improvement.
All in all, this is one of the best conferences that I've been to in that we all shared our experiences and our goals of continuous improvement. Our shared experiences make us ever more conscious of where our failures are, and what we can do to rectify. A lot of people left with more questions and answers, and that's a good thing. It keeps us mindful of the fact that we need to keep searching for answers and ways to improve. Life wouldn't be as fun if we had all the answers!
As always, my Functional C# samples are available on MSDN Code Gallery, and I will be posting my Functional F# samples shortly after I get them cleaned up a bit.