Functional approach to try-catch-finally
See original GitHub issueHello,
I find my way into functional programming and love to see, that I can get started without leaving C# behind. Thanks for the great work! But I cannot get my head around on how to implement a try-catch-finally behaviour in a FP style.
I have to following 3 functions to work with and every single one can throw an exception…
int AllocatateResource(int id)
void UseResource(int resId)
void DeleteResource(int resId)
With try catch finally it would look like this (it is important, that the finally block can exit with an exception):
int resId;
try
{
resId = AllocatateResource(1);
UseResource(resId);
}
finally
{
if(resId != 0)
{
DeleteResource(resId);
}
}
This is a real mess in my opinion so I changed those methods to the following:
Fin<int> AllocatateResource(int id)
Fin<Unit> UseResource(int resId)
Fin<Unit> DeleteResource(int resId)
Now, I can do this, which looks pretty good and is easier to understand.
return from resId in AllocatateResource(1)
from unit in UseResource(resId)
from _ in DeleteResource(resId)
select _;
But if UseResource returns an Error, there will be an early out and DeleteResource is not called anymore. If I have to use Map and Match Functions to handle this, it will get more complicated again. I tried to use the Prelude.use function and wrap the allocate and delete function in an IDisposable, but in that case the possible Error from DeleteResource cannot be accessed anymore. So my best working state is this:
return from resId in AllocatateResource(1)
from _ in UseResource(resId).Match(
Succ: unit => DeleteResource(resId),
Fail: err => DeleteResource(resId).Match(
Succ: unit => err,
Fail: error => error)) // Override the first error with the second one - same as with a throwing finally block in case of an "active" exception
select _;
The Fail part in the first match clause is my main issue, because I need the Error from the UseResource call and not the success state of the DeleteResource call. In addition I dont like the two calls to DeleteResource. Is there any smarter/better way of calling the DeleteResource method if AllocatateResource was sucessful and keep the error of the UseResource call?
Thanks for any help 😃
Issue Analytics
- State:
- Created a year ago
- Comments:6 (3 by maintainers)

Top Related StackOverflow Question
No, but the way to think about the monadic types, like
Fin,Either,Eff, etc. is that they all have a set of built-in capabilities, which make them what they are.Finhas the ‘alternative value’ capability, where the alternative value is anError, but that’s all it does.Tryhas the ‘alternative value’ capability where the alternative value is anException, but it also has the ‘catch exceptions’ capability,The trick for any part of your application is to use the monad with the smallest set of capabilities for the subdomain you’re working on. When you need to expand those you pick a ‘bigger’ monad with more built-in capabilities.
Efffor example has:Affto support asynchronyEff<RT, A>for dependency injection)It’s our IO monad. It should be at the outer edges of your application.
You can think of the smallest capability being entirely pure functions. That’s the ‘inner’ layer. Then maybe
Option, thenTry, thenEffetc. Like an onion of expanding capability.EffectandEffare not the same. It’s an unfortunate naming conflict that I’m not super happy about, but it’s there now.Effectis part of the Pipes functionality (a composedProducer,Pipe, andConsumerfuse together into anEffect); it’s probably not appropriate for what you’re doing.Eff(and it’s asynchronous variantAff) are the monads for doing IO side-effects, and has lots of built-in stuff for dealing with IO (like@catch, scheduled retries, repeats, etc.)EffandAffwill eventually gain the capability to track resources automatically, but it’s not there today, so you need to use theusefunction with the second function argument to wrap up usage of the resource.You can remove the
catchpart so exceptions are exposed normally?You’re using
Fin<A>here, andFin<A>is like anEither<Error, A>. What neitherFin, norEither, do is catch exceptions. That means there no automatic way to clean up resources. And so, you have a couple of options:The only monad that does
1right now isEffect- which I wouldn’t advise using for this. So, you can write a function calleduse:Then you can do this:
With the
Effectmonad it’s possible to just do this:It tracks the resources and cleans them up automatically. I am actually working on bringing resource tracking to every monadic type (for version 5), but for now it’s a case of wrapping up the sub-expression with your own function.
For some monadic types there are some
usefunctions already in thePrelude, just not for the value-type monads likeFin,Option, andEither.One other thing, seeing as it looks like you’re doing IO side-effects, I’d recommend using
EffandAffoverFin. They also have theusefunctions built into thePrelude- and also have the ability to catch errors, which means you won’t have to write them yourself.