Jeremy Abbott (Not the Figure Skater)

Merry Monad Xmas

I've been playing with F# and functional programming (FP) as a hobby for a few years now. For the past month I've had the privilege of working with a mentor from the F# Foundation. If you're not a member of the F# Foundation, stop reading right now and go join.

One (of many!) aspects of functional programming that's intimidated me has been the concept of monads. For some reason I've felt that the full power of FP would become clear if I could understand this concept. I have a bad habit of shying away from concepts that sound too mathematically abstract, and I always felt that monads fell into that category. It's a personal failing I'm trying to get better at. With that being said, when I got paired up with Clément, we started working on monads immediately. For my contribution the 2016 F# Advent Calendar I hope to explain how I understand monads in the hope that it might help you too!

Setting Up The Story

Santa needs to deliver presents to the millions of good children that made the nice list. Those naughty children that didn't make the cut will be receiving a lump of coal.

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: open System type Gift = | Wants of string | Coal type Person = { Name : string ; SharedToys : bool ; IsANastyWoman : bool ; Wants : string } let giftIf behavior person = let gift = if behavior then Wants person . Wants else Coal person . Name , gift let sharedToys person = giftIf person . SharedToys person let wasANastyWoman person = giftIf person . IsANastyWoman person let determineGift person = match sharedToys person with | n , Coal -> n , Coal // fail fast | _, _ -> match wasANastyWoman person with | n , Coal -> n , Coal // fail again | n , Wants s -> n , Wants s let naughtyOrNice person = person |> determineGift naughtyOrNice { Name = "Jeremy" ; // Jeremy gets a redo on 2016 IsANastyWoman = true ; SharedToys = true ; Wants = "For 2016 not to have happenend" } naughtyOrNice { Name = "Donald" ; // Donald gets coal IsANastyWoman = false ; SharedToys = false ; Wants = "To maintain systemic discrimination and privilege." }

The preceding code defines a record type with a Name property, and the criteria Santa is using this year to determine if someone is naughty or nice. Next we define some functions to validate each person, and then ultimately determine if they are naughty or nice.

The functions sharedToys and wasANastyWoman return tuples of the type string * Gift where the Gift's case is based on whether or not they were a decent person. If they weren't a decent person they get a nice lump of coal for Christmas.

The function determineGift determines whether the person gets Coal or the gift they requested. Our problem starts here:

1: 2: 3: 4: 5: 6: 7: 8: // this is not ideal let determineGift person = match sharedToys person with | n , Coal -> n , Coal // fail fast | _, _ -> match wasANastyWoman person with | n , Coal -> n , Coal // fail again | n , Wants s -> n , Wants s // what if we need to add more validation rules?

As we add more rules for determining gifts we have to keep extending this function. "Arrow code" like this is a code smell. This kind of code isn't very idiomatic in functional programming either. Yes, we have small functions that do one thing, but we don't have a nice way of composing them together.

To make this a little more idiomatic we could change Person like this:

1: type Person' = { Name : string ; SharedToys : bool ; IsANastyWoman : bool ; Gift : Gift }

And then change the validation functions to behave like this:

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: type Person' = { Name : string ; SharedToys : bool ; IsANastyWoman : bool ; Gift : Gift } let giftIf' behavior ( person : Person' ) = if behavior then person else { person with Gift = Coal } let sharedToys' person = giftIf' person . SharedToys person let wasANastyWoman' person = giftIf' person . IsANastyWoman person let determineGift' = sharedToys' > > wasANastyWoman'

Now we can add new functions to determineGift via composition. Unfortunately it's not clear what kind of gift a person gets without inspecting the Person' record. We would have to match on the shape of the record type if we wanted to filter on people who were naughty or nice.

Using a Result Type

In the previous iteration we recognized that it would be nice if the inputs and outputs to our validation functions were more uniform because it gave us the flexibility to compose our functions. There is a problem with this latest approach though: All of the validation functions are executed, which means we can't quick return if any of them fail.

The previous iteration can be improved upon with a couple more adjustments. The previous example mentioned that the caller can't tell if someone was "naughty" or "nice" without inspecting properties on the result. It would be nice if the caller could tell from the result type if the person belonged on the good list or the bad list.

Enter the Result Type

1: 2: 3: type Result < ' t > = | Nice of ' t | Naughty of ' t

If the Person was nice, the validation functions should return a result of Nice , and if they were naughty, the functions should return a result of Naughty .

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: let giftIf'' behavior ( person : Person' ) = if behavior then Nice person else Naughty { person with Gift = Coal } let sharedToys'' person = giftIf'' person . SharedToys person let wasANastyWoman'' person = giftIf'' person . IsANastyWoman person let determineGift'' person = match sharedToys'' person with | Naughty p -> Naughty p | Nice p -> match wasANastyWoman'' person with | Naughty p -> Naughty p | Nice p -> Nice p

Awesome! The output of the validation functions is now easy to match on. But, the original problem of arrow code and an inability to chain functions is back. It would be nice if the functions were chainable, and if the functions would fail fast meaning that if validation if one function fails, the logic of subsequent functions isn't executed.

1: 2: 3: 4: 5: // function that accepts a (person -> Result) and a Result let failFast nOrFF nOrF = match nOrF with | Naughty p -> Naughty p | Nice p -> nOrFF p

The function failFast accepts any function with the signature ('a -> Result<'a>) and returns a function with a signature of (Result<'a> -> Result<'a>) . Said another way it's a function that accepts ('a -> Result<'a>) and a Result<'a> and returns Result<'a> .

Using failFast we can rewrite determineGift as follows:

1: 2: 3: 4: 5: 6: let determineGiftRedux person = person |> sharedToys'' |> failFast wasANastyWoman'' determineGiftRedux { Name = "Donald" ; IsANastyWoman = false ; SharedToys = false ; Gift = Wants "To maintain systemic discrimination and privilege." }

Using the failFast function determineGift can be rewritten using a the pipe operator in something that is idiomatically F#.

Another key piece of the failFast function is that once we determine that a Person' is Naughty subsequent validation functions no longer get called. This can be really important if a validation function performs an expensive operation because it gets skipped if we already have enough information to make a determination.

We can make failFast even prettier:

1: 2: 3: 4: 5: 6: 7: 8: let ( > > = ) nOrF nOrFF = failFast nOrFF nOrF let determineGiftRedux' person = person |> sharedToys'' > > = wasANastyWoman'' determineGiftRedux' { Name = "Donald" ; IsANastyWoman = false ; SharedToys = false ; Gift = Wants "To maintain systemic discrimination and privilege." }

A few things are happening here:

A custom operator is created for failFast . It accepts a Result type on the left side of the operator, and a function of type 'a -> Result<'a> on the right side. A person is piped to the first validation function using the built-in pipe operator. This returns a Result<Person'> which can be passed to the new custom operator. The next validation function is passed as the second argument to the >>= custom operator. Additional validation functions can be included in the pipeline using >>= at this point, and they will only get called if the Result matches the Nice case.

failFast has Another Name

The failFast function has another, more idiomatic name: bind or flatMap . The name failFast makes sense for the type we're working with, but any type can implement this function and the underlying behavior could be very different. The general signature for this function is ('a -> M<'a>) -> M<'a> -> M<'b> . This function's purpose is to take a function with a signature of 'a -> M<'a> and convert it to a function with the signature M<'a> -> M<'b> . For me this was (is?) hard to grasp at first because as much as I delve into function programming I forget about key features of the style like currying and partial application.

When only the first argument is passed to bind via partial application, a new function is returned, and that function has the signature M<'a> -> M<'b> . That new function allows the caller to chain functions, threading M<'a> through as the argument. Neat!

Introducing Map

The Result type has turned out to be pretty useful for this exercise. However, how can we use the Result type in practice if we have functions that weren't written to work with Result ? For example, what if a lot of people are asking for a "redo on 2016?" That seems really valid, but isn't the most feasible gift. Santa suggests that they just need a time out with the latest Pokémon game because Pokémon makes everything better.

1: 2: 3: 4: let needsPokemon p = match p with | { Gift = Wants "For 2016 not to have happenend" } -> { p with Gift = Wants "Pokémon Sun/Moon" } | _ -> p

However, the function needsPokemon has the signature (Person' -> Person') and our pretty function pipeline only works with (Result<'t> -> Result<'t>) . This is where the map function comes in handy:

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: let map f r = match r with | Nice n -> Nice ( f n ) | Naughty n -> Naughty n let determineGiftRedux'' person = person |> sharedToys'' > > = wasANastyWoman'' |> map needsPokemon determineGiftRedux'' { Name = "Jeremy" ; IsANastyWoman = true ; SharedToys = true ; Gift = Wants "For 2016 not to have happenend" } (* Result<Person'> = Nice { Name = "Jeremy"; SharedToys = true; IsANastyWoman = true; Gift = Wants "Pokémon Sun/Moon";} *)

Using the map function we were able to take a function that wasn't designed to work with our Result pipeline and convert it into one that was.

Spoiler: Result is a Monad

So far this excercise has a allowed us to:

Cathartically rip on what has been a disappointing year Help Santa easily manage his naughty or nice lists Learn about bind (flatMap) and map Use a monadic type without really talking about monads!

While points 1 and 2 are fun, let's focus on points 3 and 4. Any type that has both a map and a bind function is actually a monadic type. Any type that implements a map function, where that map function follows a few rules, is called a functor. A monad can then be said to be a functor that also implements flatMap.

If you've worked with F# for any period of time, you've probably used a couple monadic types without knowing it. Before learning about monads, I felt like working with the Option type was nice for being explicit about what a function should accept or return, but that it was awkward in practice. Who would want to have to constantly match on Some and None ? Well, it turns out that's not really the best way to work with that type:

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: // signature is string -> Option string let optionalString s = if System . String . IsNullOrEmpty ( s ) then None else Some s // signature is string -> Option string let withCharacters s = if System . String . IsNullOrWhiteSpace ( s ) then None else Some s let empty = "" let notEmpty = "Hello fellow nasty woman" let whitespace = " " let oEmpty = optionalString empty // None let oNotEmpty = optionalString notEmpty // Some string let oWhiteSpace = withCharacters whitespace // None let shouldBeNone = whitespace |> optionalString |> Option . bind withCharacters // checkout the usage of bind! let shouldBeSome = notEmpty |> optionalString |> Option . bind withCharacters // checkout the usage of bind!

Obviously this example is pretty contrived, but the point is still valid: using Option.bind made working with functions that return Option in a pipeline much clearer.

Happy Holidays!

I hope that this simple example using a slightly modified Result example helped you start to grasp how monadic types work, and how they can be helpful. There is a wealth of knowledge on monads out there, and these are the resources that helped me out:

Last Words

If an example here is unclear, or worse, is innaccurate please submit a pull request. I write these posts to help myself as much as being a source of information for others and that doesn't work if the information is wrong.

namespace System

type Gift =

| Wants of string

| Coal



Full name: merry.Gift

union case Gift.Wants: string -> Gift

Multiple items

val string : value:'T -> string



Full name: Microsoft.FSharp.Core.Operators.string



--------------------

type string = String



Full name: Microsoft.FSharp.Core.string

union case Gift.Coal: Gift

type Person =

{Name: string;

SharedToys: bool;

IsANastyWoman: bool;

Wants: string;}



Full name: merry.Person

Person.Name: string

Person.SharedToys: bool

type bool = Boolean



Full name: Microsoft.FSharp.Core.bool

Person.IsANastyWoman: bool

Person.Wants: string

val giftIf : behavior:bool -> person:Person -> string * Gift



Full name: merry.giftIf

val behavior : bool

val person : Person

val gift : Gift

val sharedToys : person:Person -> string * Gift



Full name: merry.sharedToys

val wasANastyWoman : person:Person -> string * Gift



Full name: merry.wasANastyWoman

val determineGift : person:Person -> string * Gift



Full name: merry.determineGift

val n : string

val s : string

val naughtyOrNice : person:Person -> string * Gift



Full name: merry.naughtyOrNice

type Person' =

{Name: string;

SharedToys: bool;

IsANastyWoman: bool;

Gift: Gift;}



Full name: merry.Person'

Person'.Name: string

Person'.SharedToys: bool

Person'.IsANastyWoman: bool

Multiple items

Person'.Gift: Gift



--------------------

type Gift =

| Wants of string

| Coal



Full name: merry.Gift

val giftIf' : behavior:bool -> person:Person' -> Person'



Full name: merry.giftIf'

val person : Person'

val sharedToys' : person:Person' -> Person'



Full name: merry.sharedToys'

val wasANastyWoman' : person:Person' -> Person'



Full name: merry.wasANastyWoman'

val determineGift' : (Person' -> Person')



Full name: merry.determineGift'

Multiple items

module Result



from Microsoft.FSharp.Core



--------------------

type Result<'t> =

| Nice of 't

| Naughty of 't



Full name: merry.Result<_>



--------------------

type Result<'T,'TError> =

| Ok of ResultValue: 'T

| Error of ErrorValue: 'TError



Full name: Microsoft.FSharp.Core.Result<_,_>

union case Result.Nice: 't -> Result<'t>

union case Result.Naughty: 't -> Result<'t>

val giftIf'' : behavior:bool -> person:Person' -> Result<Person'>



Full name: merry.giftIf''

val sharedToys'' : person:Person' -> Result<Person'>



Full name: merry.sharedToys''

val wasANastyWoman'' : person:Person' -> Result<Person'>



Full name: merry.wasANastyWoman''

val determineGift'' : person:Person' -> Result<Person'>



Full name: merry.determineGift''

val p : Person'

val failFast : nOrFF:('a -> Result<'a>) -> nOrF:Result<'a> -> Result<'a>



Full name: merry.failFast

val nOrFF : ('a -> Result<'a>)

val nOrF : Result<'a>

val p : 'a

val determineGiftRedux : person:Person' -> Result<Person'>



Full name: merry.determineGiftRedux

val determineGiftRedux' : person:Person' -> Result<Person'>



Full name: merry.determineGiftRedux'

val needsPokemon : p:Person' -> Person'



Full name: merry.needsPokemon

val map : f:('a -> 'a) -> r:Result<'a> -> Result<'a>



Full name: merry.map

val f : ('a -> 'a)

val r : Result<'a>

val n : 'a

val determineGiftRedux'' : person:Person' -> Result<Person'>



Full name: merry.determineGiftRedux''

val optionalString : s:string -> string option



Full name: merry.optionalString

Multiple items

type String =

new : value:char -> string + 7 overloads

member Chars : int -> char

member Clone : unit -> obj

member CompareTo : value:obj -> int + 1 overload

member Contains : value:string -> bool

member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit

member EndsWith : value:string -> bool + 2 overloads

member Equals : obj:obj -> bool + 2 overloads

member GetEnumerator : unit -> CharEnumerator

member GetHashCode : unit -> int

...



Full name: System.String



--------------------

String(value: nativeptr<char>) : unit

String(value: nativeptr<sbyte>) : unit

String(value: char []) : unit

String(c: char, count: int) : unit

String(value: nativeptr<char>, startIndex: int, length: int) : unit

String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit

String(value: char [], startIndex: int, length: int) : unit

String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : unit

String.IsNullOrEmpty(value: string) : bool

union case Option.None: Option<'T>

union case Option.Some: Value: 'T -> Option<'T>

val withCharacters : s:string -> string option



Full name: merry.withCharacters

String.IsNullOrWhiteSpace(value: string) : bool

val empty : string



Full name: merry.empty

val notEmpty : string



Full name: merry.notEmpty

val whitespace : string



Full name: merry.whitespace

val oEmpty : string option



Full name: merry.oEmpty

val oNotEmpty : string option



Full name: merry.oNotEmpty

val oWhiteSpace : string option



Full name: merry.oWhiteSpace

val shouldBeNone : string option



Full name: merry.shouldBeNone

module Option



from Microsoft.FSharp.Core

val bind : binder:('T -> 'U option) -> option:'T option -> 'U option



Full name: Microsoft.FSharp.Core.Option.bind

val shouldBeSome : string option



Full name: merry.shouldBeSome