F# Basics – Result<'a>: or How I Learned to Stop Worrying and Love the Bind
This year, for FsAdvent, I’m taking a request and am going to write about embracing the happy path while writing F#. When coming to F# or other functional programming languages, one of the hurdles I see people face is often rethinking how they approach problems. While many posts talk about how to do this, I want to focus on one of the huge advantages – less worry and stress about the code working correctly.
For this post, I’m going to use a small example task – parsing a file on disk and summarizing some information from it. In this case, we’ll do something simple – read a CSV log file of “events” that occurred, and get the range of ID numbers from the file. Here is our input file:
Our task will be to read this file, parse it, and get the range of IDs being used. In this case, we’ll end up with 2 numbers as our result – 1928 and 1939.
When working in F#, I often start by describing the data I’ll be using, as well as the result I want to get or manipulate. Here, each row represents three things; a date, an ID, and a description of the event. We want to eventually get two numbers, a starting ID and an ending ID. In addition, we know there are a few things that could go wrong – the file might not exist, there might be no data in the file, or the data might be malformed. I’m going to start by defining a couple of types to help with this:
My goal when writing this utility will be to always write in a “happy path”. Instead of focusing on handling bad data or errors, I’m going to try to break everything up into small steps, each a function, that does one thing. I’m always going to assume my inputs are good, and my output will always either be good result or an Issue from above. Basically, I never want to think about “bad data” – I just want to write code that does something as I go.
In F#, there is a type that helps with this significantly – Result. The Result type is a discriminated union with two options; an Ok result, or an Error.
We’ll start by opening our file. In this case, we want a function that takes a filename and returns a Stream we can use for reading. File IO is one place where exception handling is nearly required, so I’m using that to create my error case:
This function will follow a common pattern – it takes an input (filename) and creates a Result: val openFile : filename:string -> Result
To parse the file, I’m going to use the CsvHelper library. This will allow me to not think about parsing the “hard parts” of CSV, like strings with embedded commas or quotes. Now we need a small function to take a stream from our openFile and convert it to a CsvReader:
Here, we know, if we have a valid stream, we’ll get a valid reader, so we can just write a simple Stream -> CsvReader
helper. We can put these together with Result.map, which allows us to take an “Ok” result and use it’s value to make a new result.
Running in FSI, with a good filename, gives us val csv : Result = Ok CsvHelper.CsvReader
. With a bad filename, you get:
val csv : Result =
…
Error
(IO
("Could not open file",
System.IO.FileNotFoundException: Could not find file
Now that our file is opened, we can move onto parsing. Again, we’ll assume we have a good input (our reader), and just write routines to just parse. In this case, I’m going to write a function to parse a single row and create an option, and a separate function to parse until we receive None, which will return our Result.
This does get a little uglier with the routine to read all of the rows. We want to make sure to close our file (we effectively pass ownership), so we need the try/finally to dispose of it anytime our routine to open succeeded. We’ll use a simple try/with to handle parsing errors. Using Seq.initInfinite and Seq.takeWhile allows us to “loop” through our data until we return None:
Our final step is to reduce the Event list to our two numbers:
Now that we have all of the pieces, we can stream these together. We will use two functions from the Result module in the core libraries to string these together – Result.map and Result.bind.
Whenever we have a function that doesn’t produce an error case, we will use map. When we have a function that takes our input and might need an error case, we will use bind. In our case, openFile and getIds both “always work” if their input are good, so we can map them. readRowsAndClose can map the data through to a list, but can also create an error case, so we will use bind. When stringing these together, we end up with:
When run via FSI, and given the input file above, this now produces:
val result : Result = Ok { Start = 1928
End = 1939 }
If we pass an invalid filename, we get a nice error:
val result : Result =
Error
(IO
("Could not open file",
System.IO.FileNotFoundException: Could not find file...
If we pass a good filename, but the file has bad data, that is also handled:
val result : Result =
Error
(DataMalformed
("Unable to parse data",
CsvHelper.TypeConversion.TypeConverterException: The conversion cannot be performed.
Text: '193d3'
By breaking this into pieces, and using Result.map and Result.bind, we were able to parse this in stages, where every stage could assume everything was “good”, and our errors propagate through cleanly at the end. Each step in the chain only has to focus on the task at hand.
F# Basics – From loops to folds
Instead of writing about Christmas trees this year, like I have for the last few years of FsAdvent, I thought I’d do a post about a topic that seems to frequently arise on the F# Software Foundation slack #beginners channel – looping and folds.
People coming to F# from other languages frequently find themselves copying the idioms of their past, and struggling with how to have things make sense in F#. Loops are a very common example.
A common thread in these discussions which seem to cause difficulty is “looping to generate a result”. This can be seen easily in the C# tutorial’s looping code. Here, a mutable value is created to hold some result (sum), a for loop iterates through a bunch of numbers, logic is placed in the loop body that mutates the value, and the result is used afterwards. While this can be ported to F# directly, it tends to be ugly in the end, as it relies on looping and on mutation.
I frequently suggest that people new to F# should strive to reduce mutation as well as code which relies on side effects (every loop body does), but confusion frequently occurs with cases like this. Recursion is frequently suggested as a solution, but its far from simple in many cases, especially when people are just starting out.
So let’s rework this a bit – and lets start small by reducing mutation. First off, we can refactor the “logic” portion of this into a function and make it pure. In this case, we want a function which will not mutate a value, but instead, return a new value each time it’s run, passing the existing input into it.
Doing this allows the “logic” to be much more easily tested, but is also an important first step. We can then remove another point of mutation – the for loop itself, by switching to a foreach loop over a sequence:
It’s typically at this point where people get stuck. Its far less obvious to see how we remove the mutable value which holds the result. Before we move forward with removing the mutation, lets port this to F#:
While this is obviously more succinct, it does exactly the same thing, using the same imperative techniques as before, at least in the core loop. In preparation to eliminate the mutation, I’m going to make this less succinct, so the intention behind what is occuring becomes more obvious.
While this does the same steps as the previous version, each temporary value is broken out into a binding, so the intent and process becomes more clear. However, we still are using a mutable to hold and mutate state each step.
Enter fold.
If we look at Seq.fold, we see that it has the following signature:
This function takes a function as input, which takes the previous state, the current value, and returns the new state. That is remarkably similar looking to our factoring of addNewConditionally – it takes the last value, the current value, and returns the new value. It also then takes a ‘State value – which is the initial state used for the first item being processed. Finally, it takes a sequence to operate over, and then returns a result at the end after processing each item.
In our case, we have all three of these, so we can easily rewrite our loop as a fold:
Note that this eliminates the need for a mutable value – each step works on the previous result, and returns a new value. We can now remove all of the temporaries, and rewrite this much more simply:
This gives us a simple, clean way to handle this example.
On a side note, in this particular case, the result type and input collection are all int values, and the initial value is the default value for int (0), so we have other, potentially simpler options. We could use Seq.reduce instead, or a Seq.filter piped into a Seq.sum. However, looking back at fold, one benefit is that the ‘State type is separate from the input type ‘T. This means we can use a fold to replace any loop that is used to generate results based off the items being looped over, even if the types don’t match.
As a simple example, lets say our requirements change. Instead of just needing the sum of the values that meet our criteria, suppose we want to generate a list of the values as strings. All we need to do is change our function to work on and return a string list, and to change our initial ‘State value.
This works the same way, but now we go from int seq to string list instead of to an int as our result.
While these are fairly simple and contrived examples, the approach taken here works for moving from any loop that generates “results” to a fold:
- Make core logic a pure function (which can be a lambda expression) – which takes the previous result, the current value, and return a new state
- Rework items being “processed” into a collection or sequence if using a “for” style loop
- Remove the mutable “running value” and the loop, and replace with a call to fold
2018 Christmas Trees – Cross Platform Edition using Avalonia
For the last couple of years, my FsAdvent posts have focused around a simple, fun little Christmas Trees application illustrating the usage of Gjallarhorn.Bindable – and for this year, I thought I’d keep the streak going – with a twist. I’m happy to announce the availability of Gjallarhorn.Bindable.Avalonia, which allows Gjallarhorn to be used with Avalonia, the cross platform XAML based UI Framework. Read more
Christmas Trees in WPF, 2017 Update
Last year, I wrote about making Christmas Trees in WPF using Gjallarhorn. The Gjallarhorn project has improved dramatically since last year, and I thought I’d update the project, demonstrating the newer API and significant improvements in usability and ease of development. I recommend skimming through the previous post, as this post expands on the information there.
This post will modernize the Christmas Tree application from last year, improving it, and highlighting the changes in Gjallarhorn, showing the dramatic improvements inspired by great projects like Elmish. My hope is that more developers will see the benefits of unidirectional architecture for UI development, and try using Elmish for web, Gjallarhorn for XAML, or other unidirectional, functional approaches to user interfaces. Read more
Christmas Trees in WPF using FSharp.ViewModule
As my contribution to the F# Advent Calendar this year, I thought I’d write a bit about one approach I often use to create WPF user interfaces in a functional style – mixing MailboxProcessor with FSharp.ViewModule.
This post will illustrate using a pair of MailboxProcessor instances mixed with FSharp.ViewModule to construct a simple application.  In the spirit of F# Advent, our application will be a small drawing application that allows us to place and decorate Christmas trees.