Last active
April 6, 2020 10:26
-
-
Save eiriktsarpalis/8d87ce971b432d80658d33768ff7b0fa to your computer and use it in GitHub Desktop.
Encoding a Maybe monad in C# using Eff, https://github.com/nessos/Eff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Maybe | |
{ | |
public struct Maybe<T> | |
{ | |
public bool HasValue { get; } | |
public T Value { get; } | |
private Maybe(T value) | |
{ | |
HasValue = true; | |
Value = value; | |
} | |
public static Maybe<T> Nothing { get; } = new Maybe<T>(); | |
public static Maybe<T> Just(T value) => new Maybe<T>(value); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Maybe | |
{ | |
using Nessos.Eff; | |
public class MaybeEffect<T> : Effect<T> | |
{ | |
public Maybe<T> Result { get; set; } | |
} | |
public static class MaybeEffect | |
{ | |
public static MaybeEffect<T> Nothing<T>() => new MaybeEffect<T> { Result = Maybe<T>.Nothing }; | |
public static MaybeEffect<T> Just<T>(T t) => new MaybeEffect<T> { Result = Maybe<T>.Just(t) }; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Maybe | |
{ | |
using Nessos.Eff; | |
using System; | |
using System.Threading.Tasks; | |
public static class MaybeEffectHandler | |
{ | |
public static Maybe<TResult> Run<TResult>(Eff<TResult> eff) | |
{ | |
switch (eff) | |
{ | |
case ExceptionEff<TResult> setException: | |
throw setException.Exception; | |
case ResultEff<TResult> setResult: | |
return Maybe<TResult>.Just(setResult.Result); | |
case DelayEff<TResult> delay: | |
return Run(delay.Continuation.MoveNext()); | |
case AwaitEff<TResult> awaitEff: | |
var handler = new MaybeEffectHandlerImpl<TResult>(awaitEff.Continuation); | |
awaitEff.Awaiter.Accept(handler); | |
return handler.Result; | |
default: | |
throw new NotSupportedException($"{eff.GetType().Name}"); | |
} | |
} | |
public static Maybe<Unit> Run(Eff eff) | |
{ | |
return Run(Helper()); | |
async Eff<Unit> Helper() { await eff; return Unit.Value; } | |
} | |
private class MaybeEffectHandlerImpl<TResult> : EffectHandler | |
{ | |
private readonly IEffStateMachine<TResult> _continuation; | |
public MaybeEffectHandlerImpl(IEffStateMachine<TResult> continuation) | |
{ | |
_continuation = continuation; | |
} | |
public Maybe<TResult> Result { get; private set; } = Maybe<TResult>.Nothing; | |
public async override Task Handle<TValue>(EffectAwaiter<TValue> awaiter) | |
{ | |
switch (awaiter.Effect) | |
{ | |
case MaybeEffect<TValue> me: | |
if (me.Result.HasValue) | |
{ | |
awaiter.SetResult(me.Result.Value); | |
var next = _continuation.MoveNext(); | |
Result = MaybeEffectHandler.Run(next); | |
} | |
break; | |
} | |
} | |
public async override Task Handle<TValue>(EffAwaiter<TValue> awaiter) | |
{ | |
var result = MaybeEffectHandler.Run(awaiter.Eff); | |
if (result.HasValue) | |
{ | |
awaiter.SetResult(result.Value); | |
var next = _continuation.MoveNext(); | |
Result = MaybeEffectHandler.Run(next); | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Maybe | |
{ | |
using System; | |
using System.Threading.Tasks; | |
class Program | |
{ | |
static async Eff<int> Divide(int m, int n) | |
{ | |
return (n == 0) ? await MaybeEffect.Nothing<int>() : await MaybeEffect.Just<int>(m / n); | |
} | |
static async Eff DivideAndReportToConsole(int m, int n) | |
{ | |
Console.Write($"Calculating {m} / {n}: "); | |
var result = await Divide(m, n); | |
Console.WriteLine($"Got {result}!"); | |
} | |
static async Eff Test() | |
{ | |
for (int i = 0; i < 10; i++) | |
{ | |
await DivideAndReportToConsole(23, 5 - i); | |
} | |
} | |
static void Main() | |
{ | |
MaybeEffectHandler.Run(Test()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment