## Wednesday, May 26, 2010

### Computation Expressions with .NET Data Types

I’m still working on my proof-of-concept and it’s going well. One thing that’s been on my mind is the desire to use computation expressions in it somewhere. So I’ve been trying to make sure I understood computation expressions well enough to use them to good purpose.

To smooth the integration, the code will have to play nice with existing .NET components written in C#, etc. To this end, I am sometimes using .NET data structures – like List<> – in places where a pure F# implementation might use list, etc. So I decided to practice with some computation expressions that mingle in these other .NET data structures.

Here is a simple example. It splits a string into a tree using various characters and string.Split. It actually does its job using side-effects on the original tree node (Neil dodges rotten tomatoes thrown by the functional programming purists.) For example, splitting the string “a.b:c.d” by colon first and then by dot would result in this tree:

And here is the code. (As always, presented "as-is" and without warranty or implied fitness of any kind; use at your own risk.)

`open System.Collections.Generic`
`open System.Text.RegularExpressions`
` `
` `
`// This is a simple tree node`
`// which uses List<>.`
`// Think of it as a proxy for`
`// some real-world problem.`
`type Tree = `
`  { Text : string`
`    mutable Children : List<Tree> option }`
`  // Syntactic sugar.`
`  static member Make (s) = `
`    { Text=s; Children=None }`
` `
` `
`// Splits the text in the node`
`// using string.split and`
`// uses it to create child nodes.`
`let splitter (c:char) =`
`  let a = [|c|]`
`  let f (t:Tree) =`
`    t.Children <- Some(new List<Tree>())`
`    Seq.iter `
`      (fun s->t.Children.Value.Add(Tree.Make(s))) `
`      (t.Text.Split(a,System.StringSplitOptions.None))`
`    t`
`  f`
` `
` `
`// This is almost like a collect,`
`// except it works through the `
`// side-effects in the splitter function.`
`type TreeCollector () =`
` `
`  member this.Bind ((lt:Tree),`
`                    (f:Tree->Tree)) =`
`    Seq.iter (fun t->(f t)|>ignore) lt.Children.Value`
`    lt`
` `
`  member this.Return v = v`
` `
` `
`// Some tests.`
` `
`let splitBar   = splitter '|'`
`let splitColon = splitter ':'`
`let splitDot   = splitter '.'`
` `
`let treeCollector = TreeCollector()`
` `
`// Build a tree by bar, colon, then dot.`
`let f0 t = treeCollector {`
`  let! x = splitBar t`
`  let! x = splitColon x`
`  let! x = splitDot x`
`  return x`
`}`
` `
`// Build a tree by colon, bar, then dot.`
`let f1 t = treeCollector {`
`  let! x = splitColon t`
`  let! x = splitBar x`
`  let! x = splitDot x`
`  return x`
`}`
` `
` `
`// Simple test print.`
`let printo (t:Tree) =`
`  let rec f s (t:Tree) =`
`    printfn "%s %s" s t.Text `
`    if t.Children.IsSome then`
`      Seq.iter (f (s+"  ")) t.Children.Value`
`  f "" t`
` `
` `
`let t0 = Tree.Make("a0.a1:b0.b1|c0.c1:d0.d1")`
`let t1 = Tree.Make(t0.Text)`
` `
`f0 t0 |> ignore`
`f1 t1 |> ignore`
` `
`printo t0`
`printfn ""`
`printo t1`
` `
`printfn "Done."`