0

I have a recursive function that contains a series of matches that either make the recursive call back to the function, or make a call to failwith.

This is basically a hybrid implementation of the recursive descent parser descibed in Don Syme's Expert F# book (page 180) and the parsing example shown here: http://fsharpforfunandprofit.com/posts/pattern-matching-command-line/

Here is a snippet of my own code.

let rec parseTokenListRec tokenList optionsSoFar =
    match tokenList with
    | [] -> optionsSoFar
    | SOURCE::t -> 
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with Source = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the source argument."
    | REGISTRY::t ->
...

A full code listing can be found at http://fssnip.net/nU

The way the code is currently written, when the function has finished working its way through the tokenList, it will return the optionsSoFar record that has been compiled via the object expression {optionsSoFar with Source = (returnConcatHeadValues t)}, or it will throw an exception if an invalid argument is found.

I want to refactor this so that the function does not rely on an exception, but will always return a value of some sort that can be handled by the calling function. The idea I have is to return a discriminated union rather than a record.

This discriminated union would be something like

type Result =
    |Success of Options
    |Failure of string

The problem I had when I tried to refactor the code was that I couldn't figure out how to get the success value of the DU to initialize via an object expression. Is this possible?

The examples I've looked at on MSDN (http://msdn.microsoft.com/en-us/library/vstudio/dd233237(v=vs.100).aspx), fsharpforfunandprofit (http://fsharpforfunandprofit.com/posts/discriminated-unions/) and elsewhere haven't quite cleared this up for me.

I'm worried that I'm not making any sense here. I'm happy to clarify if needed.

Joe
  • 664
  • 4
  • 18

1 Answers1

2

If I understand it correctly, in you current solution, the type of optionsSoFar is Options. The code becomes trickier if you change the type of optionsSoFar to your newly defined Result.

However, I think you do not need to do that - you can keep optionsSoFar : Options and change the function to return Result. This works because you never need to call the function recursively after it fails:

let rec parseTokenListRec tokenList optionsSoFar =
    match tokenList with
    | [] -> Success optionsSoFar
    | SOURCE::t -> 
        match t with
        | VALUE x::tt -> 
            {optionsSoFar with Source = (returnConcatHeadValues t)}
            |> parseTokenListRec (returnNonValueTail t) 
        | _ -> Failure "Expected a value after the source argument."
    | REGISTRY::t -> ...

If you actually wanted to update Source in a Result value, then I'd probably write something like:

module Result = 
  let map f = function
    | Success opt -> f opt
    | Failure msg -> Failure msg

Then you could write a transformation as follows:

resultSoFar
|> Result.map (fun opts -> {opts with Source = returnConcatHeadValues t})
|> parseTokenListRec (returnNonValueTail t) 
Tomas Petricek
  • 225,798
  • 19
  • 345
  • 516
  • 2
    I was literally walking down the hallway reading your F# Computation Expression Zoo paper when I noticed that it was you who answered this question. That kind of made my day. Thanks man! – Joe Sep 12 '14 at 22:31
  • I found a great blog article on this topic: [Railway Oriented Programming](http://fsharpforfunandprofit.com/posts/recipe-part2/) – Christopher Stevenson Sep 15 '14 at 14:10