Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retry monad and Zero construct

I am trying to use the Retry Monad I took from our beloved stack overflow:

type RetryBuilder(max, sleep : TimeSpan) = 
      member x.Return(a) = a
      member x.Delay(f) = f
      member x.Zero() = failwith "Zero"
      member x.Run(f) =
        let rec loop(n) = 
            if n = 0 then failwith "Failed"
            else 
                try f() 
                with ex -> 
                    sprintf "Call failed with %s. Retrying." ex.Message |> printfn "%s"
                    Thread.Sleep(sleep); 
                    loop(n-1)
        loop max

I would like to use it to make my file copy code a bit more robust:

let retry = RetryBuilder(3, TimeSpan.FromSeconds(1.))
retry {
    System.IO.File.Move("a", "b")
}

Now I noticed that it sometimes fails with "Zero" exception. I tried to remove the member x.Zero() = failwith "Zero" but now I get a compile time error:

This construct can be only used if builder defines a 'Zero' method.

Any ideas how to proceed?

like image 530
Klark Avatar asked Mar 31 '26 16:03

Klark


2 Answers

Lee suggested that you can use return () at the end of computations that would otherwise throw, because they call the Zero member. This is a good trick - but you can actually integrate this directly into the computation builder.

The Zero member is used when your computation ends without returning. You can change it to do the same thing as return ():

type RetryBuilder(max, sleep : TimeSpan) = 
  member x.Return(a) = ...
  member x.Zero() = x.Return( () )

Then you can just write the original code and you will get unit result back:

let retry = RetryBuilder(3, TimeSpan.FromSeconds(1.))
retry {
  System.IO.File.Move("a", "b")
}
like image 144
Tomas Petricek Avatar answered Apr 02 '26 06:04

Tomas Petricek


It looks like the simplest fix is to return a value at the end:

retry {
    System.IO.File.Move("a", "b")
    return ()
}

If you look at how computation expressions are de-sugared your code seems to be converted into

retry.Run(retry.Delay(fun () -> System.IO.File.Move("a", "b"); retry.Zero()))

this causes the exception to be thrown during the evaluation. If you return a value this will not happen.

like image 25
Lee Avatar answered Apr 02 '26 05:04

Lee