Skip to content

Commit

Permalink
Breaking change: Improved Reader error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
louthy committed Aug 21, 2019
1 parent 55f36a3 commit 11d3659
Show file tree
Hide file tree
Showing 9 changed files with 614 additions and 225 deletions.
438 changes: 302 additions & 136 deletions LanguageExt.CodeGen/ReaderGenerator.cs

Large diffs are not rendered by default.

40 changes: 21 additions & 19 deletions LanguageExt.Core/ClassInstances/Monad/MReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,41 @@ public struct MReader<Env, A> :
public MB Bind<MONADB, MB, B>(Reader<Env, A> ma, Func<A, MB> f) where MONADB : struct, Monad<Env, Unit, MB, B> =>
default(MONADB).Run(env =>
{
var (a, faulted) = ma(env);
if (faulted) return default(MONADB).Fail();
return f(a);
var resA = ma(env);
if (resA.IsFaulted) return default(MONADB).Fail(resA.ErrorInt);
return f(resA.Value);
});

[Pure]
public MB BindAsync<MONADB, MB, B>(Reader<Env, A> ma, Func<A, MB> f) where MONADB : struct, MonadAsync<Env, Unit, MB, B> =>
default(MONADB).RunAsync(env =>
{
var (a, faulted) = ma(env);
if (faulted) return default(MONADB).Fail().AsTask();
return f(a).AsTask();
var resA = ma(env);
if (resA.IsFaulted) return default(MONADB).Fail(resA.ErrorInt).AsTask();
return f(resA.Value).AsTask();
});

[Pure]
public Reader<Env, A> Fail(object err = null) =>
new Reader<Env, A>(_ => (default(A), true));
public Reader<Env, A> Fail(object err = null) => _ =>
err is Common.Error error ? ReaderResult<A>.New(error)
: err is Exception exception ? ReaderResult<A>.New(Common.Error.New(exception))
: ReaderResult<A>.Bottom;

[Pure]
public Reader<Env, A> Reader(Func<Env, A> f) => env =>
(f(env), false);
public Reader<Env, A> Reader(Func<Env, A> f) => env =>
ReaderResult<A>.New(f(env));

[Pure]
public Reader<Env, Env> Ask() => env =>
(env, false);
ReaderResult<Env>.New(env);

[Pure]
public Reader<Env, A> Local(Reader<Env, A> ma, Func<Env, Env> f) => env =>
ma(f(env));

[Pure]
public Reader<Env, A> Return(Func<Env, A> f) => env =>
(f(env), false);
ReaderResult<A>.New(f(env));

[Pure]
public Reader<Env, A> Run(Func<Env, Reader<Env, A>> f) => env =>
Expand All @@ -58,23 +60,23 @@ public Reader<Env, A> Run(Func<Env, Reader<Env, A>> f) => env =>
[Pure]
public Reader<Env, A> Plus(Reader<Env, A> ma, Reader<Env, A> mb) => env =>
{
var (a, faulted) = ma(env);
return faulted
var resA = ma(env);
return resA.IsFaulted
? mb(env)
: (a, faulted);
: resA;
};

[Pure]
public Reader<Env, A> Zero() =>
_ => (default(A), true);
_ => ReaderResult<A>.Bottom;

[Pure]
public Func<Env, S> Fold<S>(Reader<Env, A> fa, S state, Func<S, A, S> f) => env =>
{
var (a, faulted) = fa(env);
return faulted
var resA = fa(env);
return resA.IsFaulted
? state
: f(state, a);
: f(state, resA.Value);
};

[Pure]
Expand Down
49 changes: 49 additions & 0 deletions LanguageExt.Core/Common/Error.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using static LanguageExt.Prelude;

namespace LanguageExt.Common
{
public struct Error
{
public readonly static Error Bottom = new Error(666, "Bottom", None);

readonly int code;
readonly string message;
public readonly Option<Exception> Exception;

Error(int code, string message, Option<Exception> exception)
{
this.code = code;
this.message = message ?? throw new ArgumentNullException(nameof(message));
Exception = exception;
}

public int Code =>
message == null
? 666
: code;

public string Message =>
message ?? "Bottom";

public static Error New(int code, string message, Option<Exception> exception) =>
new Error(code, message, exception);

public static Error New(Exception exception) =>
new Error(exception.HResult, exception.Message, exception);

public static Error New(string message, Exception exception) =>
new Error(exception.HResult, message, exception);

public static Error New(string message) =>
new Error(0, message, None);

public static Error New(int code, string message) =>
new Error(code, message, None);

internal Exception ToException() =>
Exception.IsSome
? (Exception)Exception
: new Exception(Message);
}
}
95 changes: 62 additions & 33 deletions LanguageExt.Core/DataTypes/Reader/Reader.Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using static LanguageExt.Prelude;
using System.Diagnostics.Contracts;
using LanguageExt.ClassInstances;
using System.Collections.Generic;

/// <summary>
/// Reader monad extensions
Expand All @@ -21,25 +22,17 @@ public static Reader<Env, A> Flatten<Env, A>(this Reader<Env, Reader<Env, A>> ma
/// Runs the Reader monad and memoizes the result in a TryOption monad. Use
/// Match, IfSucc, IfNone, etc to extract.
/// </summary>
public static TryOption<A> Run<Env, A>(this Reader<Env, A> self, Env env)
public static ReaderResult<A> Run<Env, A>(this Reader<Env, A> self, Env env)
{
try
{
if (self == null) return () => Option<A>.None; ;
if (env == null) return () => Option<A>.None; ;
var (a, b) = self(env);
if(b)
{
return () => Option<A>.None;
}
else
{
return () => Optional(a);
}
if (self == null) throw new ArgumentNullException(nameof(self));
if (env == null) throw new ArgumentNullException(nameof(env));
return self(env);
}
catch(Exception e)
{
return () => new OptionalResult<A>(e);
return ReaderResult<A>.New(Error.New(e));
}
}

Expand All @@ -49,10 +42,58 @@ public static Reader<Env, Seq<A>> AsEnumerable<Env, A>(this Reader<Env, A> self)

[Pure]
public static Seq<A> ToSeq<Env, A>(this Reader<Env, A> self, Env env) =>
self.Map(x => x.Cons()).Run(env).IfNoneOrFail(Empty);
self.Run(env).ToSeq();

[Pure]
public static Seq<A> AsEnumerable<Env, A>(this Reader<Env, A> self, Env env) =>
public static Lst<A> ToList<Env, A>(this Reader<Env, A> self, Env env) =>
self.Run(env).ToList();

[Pure]
public static Option<A> ToOption<Env, A>(this Reader<Env, A> self, Env env) =>
self.Run(env).ToOption();

[Pure]
public static OptionUnsafe<A> ToOptionUnsafe<Env, A>(this Reader<Env, A> self, Env env) =>
self.Run(env).ToOptionUnsafe();

[Pure]
public static OptionAsync<A> ToOptionAsync<Env, A>(this Reader<Env, A> self, Env env) =>
self.Run(env).ToOptionAsync();

[Pure]
public static Either<Error, A> ToEither<Env, A>(this Reader<Env, A> self, Env env) =>
self.Run(env).ToEither();

[Pure]
public static Either<L, A> ToEither<Env, L, A>(this Reader<Env, A> self, Env env, Func<Error, L> Left) =>
self.Run(env).ToEither(Left);

[Pure]
public static EitherUnsafe<Error, A> ToEitherUnsafe<Env, A>(this Reader<Env, A> self, Env env) =>
self.Run(env).ToEitherUnsafe();

[Pure]
public static EitherUnsafe<L, A> ToEitherUnsafe<Env, L, A>(this Reader<Env, A> self, Env env, Func<Error, L> Left) =>
self.Run(env).ToEitherUnsafe(Left);

[Pure]
public static EitherAsync<Error, A> ToEitherAsync<Env, A>(this Reader<Env, A> self, Env env) =>
self.Run(env).ToEitherAsync();

[Pure]
public static EitherAsync<L, A> ToEitherAsync<Env, L, A>(this Reader<Env, A> self, Env env, Func<Error, L> Left) =>
self.Run(env).ToEitherAsync(Left);

[Pure]
public static Try<A> ToTry<Env, A>(this Reader<Env, A> self, Env env) =>
self.Run(env).ToTry();

[Pure]
public static TryAsync<A> ToTryAsync<Env, A>(this Reader<Env, A> self, Env env) =>
self.Run(env).ToTryAsync();

[Pure]
public static IEnumerable<A> AsEnumerable<Env, A>(this Reader<Env, A> self, Env env) =>
ToSeq(self, env);

public static Reader<Env, Unit> Iter<Env, A>(this Reader<Env, A> self, Action<A> action) =>
Expand Down Expand Up @@ -82,26 +123,14 @@ public static Reader<Env, S> Fold<Env, S, A>(this Reader<Env, A> self, S state,
public static Reader<Env, R> Map<Env, A, R>(this Reader<Env, A> self, Func<A, R> mapper) =>
self.Select(mapper);

[Pure]
public static Reader<Env, A> Filter<Env, A>(this Reader<Env, A> self, Func<A, bool> pred) =>
self.Where(pred);

[Pure]
public static Reader<Env, A> Where<Env, A>(this Reader<Env, A> self, Func<A, bool> pred) => env =>
{
var (a, faulted) = self(env);
if (faulted || !pred(a)) return (a, true);
return (a, false);
};

/// <summary>
/// Force evaluation of the monad (once only)
/// </summary>
[Pure]
public static Reader<Env, A> Strict<Env, A>(this Reader<Env, A> ma)
{
Option<(A, bool)> cache = default;
object sync = new object();
Option<ReaderResult<A>> cache = default;
var sync = new object();
return env =>
{
if (cache.IsSome) return cache.Value;
Expand Down Expand Up @@ -160,10 +189,10 @@ public static Reader<Env, C> SelectMany<Env, A, B, C>(
public static Reader<Env, Env> Fold<Env, A>(this Reader<Env, A> self, Func<Env, A, Env> f) =>
env =>
{
var (x, b) = self(env);
return b
? (default(Env), true)
: (f(env, x), false);
var resA = self(env);
return resA.IsFaulted
? ReaderResult<Env>.New(resA.ErrorInt)
: ReaderResult<Env>.New(f(env, resA.Value));
};
}

40 changes: 34 additions & 6 deletions LanguageExt.Core/DataTypes/Reader/Reader.Prelude.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,34 @@ public static Reader<Env, A> Reader<Env, A>(A value) =>
public static Reader<Env, A> Reader<Env, A>(Func<Env, A> f) =>
default(MReader<Env, A>).Return(f);

/// <summary>
/// Reader failure
/// </summary>
[Pure]
public static Reader<Env, A> ReaderFail<Env, A>(string error) => env =>
ReaderResult<A>.New(Common.Error.New(error));

/// <summary>
/// Reader failure
/// </summary>
[Pure]
public static Reader<Env, A> ReaderFail<Env, A>(int code, string error) => env =>
ReaderResult<A>.New(Common.Error.New(code, error));

/// <summary>
/// Reader failure
/// </summary>
[Pure]
public static Reader<Env, A> ReaderFail<Env, A>(string error, Exception exception) => env =>
ReaderResult<A>.New(Common.Error.New(error, exception));

/// <summary>
/// Reader failure
/// </summary>
[Pure]
public static Reader<Env, A> ReaderFail<Env, A>(Exception exception) => env =>
ReaderResult<A>.New(Common.Error.New(exception));

/// <summary>
/// Retrieves the reader monad environment.
/// </summary>
Expand Down Expand Up @@ -74,13 +102,13 @@ public static Reader<Env, Option<A>> choose<Env, A>(params Reader<Env, Option<A>
{
foreach (var monad in monads)
{
var (x, bottom) = monad(state);
if (!bottom && x.IsSome)
var resA = monad(state);
if (!resA.IsFaulted)
{
return (x, bottom);
return resA;
}
}
return (default(A), true);
return ReaderResult<Option<A>>.Bottom;
};

/// <summary>
Expand All @@ -94,9 +122,9 @@ public static Reader<Env, A> tryread<Env, A>(Reader<Env, A> m) =>
{
return m(state);
}
catch
catch(Exception e)
{
return (default(A), true);
return ReaderResult<A>.New(Common.Error.New(e));
}
};

Expand Down
Loading

0 comments on commit 11d3659

Please sign in to comment.