Ideas to have continuation-styled monads for Option/Either/etc?
See original GitHub issueHi. I’ve recently read this article http://blog.paralleluniverse.co/2015/08/07/scoped-continuations/ and I’ve come to agree with its conclusion: In imperative languages monads are better suited to be used and composed using delimited/scoped continuations. In my actual job I’ve introduced the Option and Either monads using LINQ, but we encountered problems (mainly in maintainability and readability). These issues were improved when I started using continuation-styled Options and Eithers.
Do you believe they could be incorporated into this library?
I’ll explain the proposal better:
Imagine you have this code that uses the Option monad in C# with LINQ:
public Option<string> Foo(Option<int> a, Option<int> b)
{
return
from aVal in a
from bVal in b
let c = aVal + bVal
select c.ToString();
}
It seems pretty straight-forward. However, what if, before returning c.ToString()
, we actually need to call a procedure Bar()
imperatively? It gets harder to do so in a readable way.
What if Options were defined using continuations? (using more pseudo-syntax than valid C#)
public static T Value<T,cps<K,K>>(this Option<T> opt)
{
return shift { (Func<T, K> k) => opt.Match(
None: None // return type : Option<K>
Some: (T t) => Some(k(t)) // return type : Option<K>
) };
}
public static Option<T> OptionHandle<T>(Func<T> f)
{
return reset { f() };
}
public Option<string> Foo(Option<int> a, Option<int> b)
{
return OptionHandle(() =>
{
var aVal = a.Value();
var bVal = b.Value();
var c = aVal + bVal;
Bar();
return c.ToString();
});
}
Everything inside the delimited continuation or OptionHandle
will work inside the Option monad, but you are able to use the full power of the imperative language. You maintain a relatively readable stacktrace (e.g if Bar()
throws an exception), you can call imperative code from inside of it, and it can create more readable code, specially if you have to compose various different monads together (in my case it was composing both Either and Option in various ways).
C# doesn’t have continuations like the above, but since the continuations are one-shot you can simulate the above pretty well using exceptions:
public class OptionException : Exception {}
public static T Value<T>(this Option<T> opt)
{
return opt.Match(
None: () => { throw new OptionException();},
Some: t => t);
}
public static Option<T> OptionHandle<T>(Func<T> f)
{
try
{
return f();
} catch (OptionException)
{
return None;
}
}
Using exceptions and try-catch you get the same behavior.
Issue Analytics
- State:
- Created 4 years ago
- Comments:10 (3 by maintainers)
Top GitHub Comments
Actually it has - it’s called async/await. It requires
Option<T>
to provide GetAwaiter() method + async generator to support it, but I think something like this should be possible.What about this case?
Which type would be inferred? Which type signature would compile (if it compiles at all)?
Task<Option<int>>
orOption<Task<int>>
?