fix TaskBuilder.TryFinally -- fixes #348 by sideeffffect · Pull Request #349 · fsprojects/FSharpx.Extras · GitHub
Skip to content

fix TaskBuilder.TryFinally -- fixes #348#349

Merged
forki merged 1 commit into
fsprojects:masterfrom
sideeffffect:fix-TaskBuilder-TryFinally
Oct 31, 2016
Merged

fix TaskBuilder.TryFinally -- fixes #348#349
forki merged 1 commit into
fsprojects:masterfrom
sideeffffect:fix-TaskBuilder-TryFinally

Conversation

@sideeffffect

Copy link
Copy Markdown
Contributor

Fixes the uses of use keyword in TaskBuilder and other constructs that use TryFinally.
Before, TryFinally called compensation (disposing) immediately.
Now the compensation (disposing) is called only after the body runs.

fixes #348

@sideeffffect sideeffffect force-pushed the fix-TaskBuilder-TryFinally branch 2 times, most recently from 1f7aa39 to b999db3 Compare October 23, 2016 03:09
@sideeffffect

Copy link
Copy Markdown
Contributor Author

@rspeele

rspeele commented Oct 23, 2016

Copy link
Copy Markdown

Hi,

I applaud you for actually contributing changes (rather than just whining like me).

I can answer your second question at least: the type signatures in the doc are really just suggestions. All that matters is that everything typechecks after the compiler desugars the computation expression to method calls. This means you can use a separate type to represent a delayed expression as long as the methods that will take delayed expressions handle that type. For example, the body of a while loop is always delayed, so while you could write (as the docs suggest):

member __.Delay(computation : unit -> M<'t>) = ... : M<'t>
member __.While(cond : unit -> bool, delayedBody : M<unit>) = ... : M<unit>

This approach works just as well (as long as you don't need any special custom behavior for your delayed exprs), and is probably the most common way to do it:

member __.Delay(computation : unit -> M<'t>) = computation
member __.While(cond : unit -> bool, delayedBody : unit -> M<unit>) = ... : M<unit>
                                                // ^^^^^^^^^^^^^^^
                                                // notice the type matches delay's return type

Anyway, from skimming your contribution, I think you fell victim to a bug that I also had with my own TaskBuilder implementation. If m completes without encountering any exceptions, but then an exception is thrown by compensation() in wrapOk, it's still in your TryWith so you'll run compensation() a second time via wrapCarsh (was this supposed to be named wrapCrash?). Concrete example:

try
    ()
finally
    printfn "ran!" // I think this will get printed twice.
    failwith "uhoh"

I think if you just move the Bind to the outside like this, that should fix the problem. Since wrapCrash raises its exception again you know that execution won't proceed to wrapOk if wrapCrash ran.

// instead of
// this.TryWith((fun () -> this.Bind(m(), wrapOk)), wrapCrash)
   this.Bind(this.TryWith(m, wrapCrash), wrapOk)

@sideeffffect sideeffffect force-pushed the fix-TaskBuilder-TryFinally branch 3 times, most recently from 50a04f9 to d16572e Compare October 23, 2016 22:58
@sideeffffect

Copy link
Copy Markdown
Contributor Author

@rspeele thanks so much for spotting the mistake and the general overview! I think I'm getting now much better what's going on.
but what I still don't understand is why does Delay have to be fun () -> this.Bind(this.Return(), f) -- why is fun () -> f() not enough? or even just f?

@sideeffffect

Copy link
Copy Markdown
Contributor Author

@forki @mausch @panesofglass ping

Fixes the uses of `use` keyword in `TaskBuilder` and other constructs that use `TryFinally`.
Before, `TryFinally` called compensation (disposing) immediately.
Now the compensation (disposing) is called only after the body runs.

fixes fsprojects#348
@sideeffffect sideeffffect force-pushed the fix-TaskBuilder-TryFinally branch from d16572e to 9e0b9fd Compare October 30, 2016 16:46
@forki

forki commented Oct 31, 2016

Copy link
Copy Markdown
Member

thx

@forki forki merged commit 9e0b9fd into fsprojects:master Oct 31, 2016
@sideeffffect sideeffffect deleted the fix-TaskBuilder-TryFinally branch October 31, 2016 09:50
@sideeffffect

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TaskBuilder.TryFinally runs finally block immediately

3 participants