Grim's Scythe
Marcus Griep

Writings on software engineering.

Recent posts


Synchronous messaging and lightweight threading


Powerful JSON processing with computation expressions


Using Chiron to serialize types that you can't control


Getting started with Chiron


Getting started — all over again

Chiron: Easier with Computation Expressions

Back in my first Chiron article, I made a bit of a mistake. I started with all the things that scare off new functional programmers. Things like >>= and monads. We're going to take a step back from that precipice and talk about the json{} computation expression.

In general, computation expressions are a bit of syntactic sugar that F# provides. Internally, the monadic functions of Bind and Return are used, but that is generally transparent to the user of a computation expression.

Let's dive straight into an example from the first article:

1: 
2: 
3: 
type User =
  { Name: string
    IsAdmin: bool }

Now, using the json{} compuation expression, let's add the ToJson and FromJson functions:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
  static member ToJson (x:User) = json {
    do! Json.write "name" x.Name
    do! Json.write "isAdmin" x.IsAdmin
  }
  static member FromJson (_:User) = json {
    let! n = Json.read "name"
    let! a = Json.read "isAdmin"
    return { Name = n; IsAdmin = a }
  }

Compared to the previous example, I think this is way more understandable on its face. There are no custom operators to figure out, and you don't need to open Chiron.Operators to use it.

So, what do the various parts mean? What's that let! and do!? For a fuller explanation of computation expressions in general, I'll point you to the excellent introduction by Scott Wlaschin on fsharpforfunandprofit.com. Nonetheless, let's briefly break this down.

1: 
    let! n = Json.read "name"

The first line that we will look at is from the FromJson function. In this case, we are reading the name member from the hidden Json object. This deserialization may or may not be successful. Presuming that it is successful, we want the value to be bound to n, but if it fails, we want to short-circuit and report the error.

The key to making this work is the ! in let!. The ! provides a signal to the F# compiler that what we really want is the string from the Json<string> returned by Json.read. Chiron's json{} implementation of provides such a bind function which takes care of unwrapping the deserialized value, or in case deserialization fails, triggers the short-circuit to return the deserialization error.

1: 
    do! Json.write "name" x.Name

Things are subtly different in the ToJson function. In this case, we are updating the hidden Json object being held by the computation expression. The write doesn't return anything meaningful but it does have a return type of Json<unit>. The above is syntactically equivalent to:

1: 
    let! _ = Json.write "name" x.Name

The do! just plain looks better than binding to an ignored value.

In the long-promised next article, we'll finally delve into dealing with missing data and union types.

Multiple items
val string : value:'T -> string

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

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool