Wrangling TryWith in Computation expressions

Benjol

(Having failed to 'grok' FParsec, I followed the advice I read somewhere and started trying to write a little parser myself. Somehow I spotted what looked like a chance to try and monadify it, and now I have N problems...)

This is my 'Result' type (simplified)

type Result<'a> = 
    | Success of 'a
    | Failure of string

Here's the computation expression builder

type ResultBuilder() =
    member m.Return a = Success(a)
    member m.Bind(r,fn) =
        match r with
        | Success(a) -> fn a
        | Failure(m) -> Failure(m)

In this first example, everything works (compiles) as expected:

module Parser = 
    let res = ResultBuilder()

    let Combine p1 p2 fn = 
        fun a -> res { let! x = p1 a
                       let! y = p2 a
                       return fn(x,y) }

My problem is here: I'd like to be able to catch any failure in the 'combining' function and return a failure, but it says that I should define a 'Zero'.

    let Combine2 p1 p2 fn =
        fun a -> res { let! x = p1 a
                       let! y = p2 a
                       try
                          return fn(x,y) 
                       with
                         | ex -> Failure(ex.Message) }

Having no idea what I should return in a Zero, I just threw in member m.Zero() = Failure("hello world"), and it now says I need TryWith.

So:

member m.TryWith(r,fn) =
    try 
        r()
    with
     | ex -> fn ex

And now it wants Delay, so member m.Delay f = (fun () -> f()).

At which point it says (on the ex -> Failure), This expression should have type 'unit', but has type 'Result<'a>', and I throw up my arms and turn to you guys...

Link for playing: http://dotnetfiddle.net/Ho1sGS

V.B.

The with block should also return a result from the computation expression. Since you want to return Result.Failure you need to define the member m.ReturnFrom a = a and use it to return the Failure from the with block. In the try block you should also specify that fn returns Success if it doesn't throw.

let Combine2 p1 p2 fn =
            fun a -> res { let! x = p1 a
                           let! y = p2 a
                           return! 
                                try
                                    Success(fn(x,y))
                                with
                                    | ex -> Failure(ex.Message)
                         }

Update:

The original implementation was showing a warning, not an error. The expression in the with block was not used since you returned from the try block, so you could simply add |> ignore. In that case if fn throws then the return value is m.Zero() and the only difference is that you would get "hello world" instead of ex.Message. Illustrated with an example below. Full script here: http://dotnetfiddle.net/mFbeZg

Original implementation with |> ignore to mute the warning:

let Combine3 p1 p2 fn =
            fun a -> res { let! x = p1 a
                           let! y = p2 a

                           try
                                return fn(x,y)
                           with
                                | ex -> Failure(ex.Message) |> ignore // no warning
                         }

Run it:

let comb2 a  =
    let p1' x = Success(x)
    let p2' y = Success(y)
    let fn' (x,y) = 1/0 // div by zero
    let func = Parser.Combine2 p1' p2' fn' a
    func()

let comb3 a  =
    let p1' x = Success(x)
    let p2' y = Success(y)
    let fn' (x,y) = 1/0 // div by zero
    let func = Parser.Combine3 p1' p2' fn' a
    func()

let test2 = comb2 1
let test3 = comb3 1

Result:

val test2 : Result<int> = Failure "Attempted to divide by zero."
val test3 : Result<int> = Failure "hello world"

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Wrangling TryWith in Computation expressions

From Dev

the mechanics of let! in computation expressions

From Dev

Computation expressions for a Haskell programmer

From Dev

Mutual recursion with computation expressions

From Dev

F# : Monad operations without computation expressions

From Dev

Intuition behind calling Zero for else branch of if..then construct in computation expressions

From Dev

Are Computation Expressions an alternative approach to Aspect-oriented Programming?

From Dev

In the theory of computation, why are regular expressions (ab)* and a*b* not equal?

From Dev

Python data wrangling issues

From Dev

Wrangling shifted DataFrame with Pandas

From Dev

Data wrangling with python pandas

From Dev

JSAT: Data wrangling / manipulating

From Dev

Data wrangling with Weka

From Dev

Data table wrangling

From Java

Data wrangling with Python Pandas and pivoting

From Dev

Dataframe Wrangling with Dates and Periods in Pandas

From Dev

Data Wrangling for Analysis Services Cube

From Dev

Parallel computation

From Dev

error running python script for data wrangling

From Dev

Java Apache Spark flatMaps & Data Wrangling

From Dev

error running python script for data wrangling

From Dev

Data Wrangling in Excel - Rearranging Columns and Rows

From Dev

Reuse results of first computation in second computation

From Dev

Interactive report: New Computation based on old Computation

From Dev

Geodesic computation on triangle meshes?

From Java

Speed up Computation

From Dev

Parallel Computation Overhead

From Dev

Exact computation and plot

From Dev

Caching expensive computation in elixir