diff --git a/LanguageExt.CodeGen/ReaderGenerator.cs b/LanguageExt.CodeGen/ReaderGenerator.cs index c6fa7cc89..d733aace5 100644 --- a/LanguageExt.CodeGen/ReaderGenerator.cs +++ b/LanguageExt.CodeGen/ReaderGenerator.cs @@ -106,28 +106,44 @@ public Task> GenerateAsync( ArgumentList( SingletonSeparatedList( Argument( - SimpleLambdaExpression( - Parameter( - Identifier("env")), - TupleExpression( - SeparatedList( - new SyntaxNodeOrToken[]{ - Argument( - IdentifierName("value")), - Token(SyntaxKind.CommaToken), - Argument( - LiteralExpression( - SyntaxKind.FalseLiteralExpression))}))))))))) + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("Prelude"), + GenericName( + Identifier("Reader")) + .WithTypeArgumentList( + TypeArgumentList( + SeparatedList( + new SyntaxNodeOrToken[]{ + ParseTypeName(envType), + Token(SyntaxKind.CommaToken), + IdentifierName(genA)}))))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + IdentifierName("value"))))))))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); - // Fail :: Unit -> MA - var failMethod = PropertyDeclaration(structA, Identifier(failName)) + // Fail :: string -> MA + var failMethod1 = MethodDeclaration( + structA, + Identifier(failName)) .WithModifiers( TokenList( new[]{ Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)})) + .WithParameterList( + ParameterList( + SingletonSeparatedList( + Parameter( + Identifier("message")) + .WithType( + PredefinedType( + Token(SyntaxKind.StringKeyword)))))) .WithExpressionBody( ArrowExpressionClause( ObjectCreationExpression(structA) @@ -135,20 +151,120 @@ public Task> GenerateAsync( ArgumentList( SingletonSeparatedList( Argument( - SimpleLambdaExpression( - Parameter( - Identifier("env")), - TupleExpression( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("Prelude"), + GenericName( + Identifier("ReaderFail")) + .WithTypeArgumentList( + TypeArgumentList( + SeparatedList( + new SyntaxNodeOrToken[]{ + ParseTypeName(envType), + Token(SyntaxKind.CommaToken), + IdentifierName(genA)}))))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + IdentifierName("message"))))))))))) + .WithSemicolonToken( + Token(SyntaxKind.SemicolonToken)); + + // Fail :: Exception -> MA + var failMethod2 = MethodDeclaration( + structA, + Identifier(failName)) + .WithModifiers( + TokenList( + new[]{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword)})) + .WithParameterList( + ParameterList( + SingletonSeparatedList(Parameter(Identifier("exception")).WithType(IdentifierName("Exception"))))) + .WithExpressionBody( + ArrowExpressionClause( + ObjectCreationExpression(structA) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("Prelude"), + GenericName( + Identifier("ReaderFail")) + .WithTypeArgumentList( + TypeArgumentList( + SeparatedList( + new SyntaxNodeOrToken[]{ + ParseTypeName(envType), + Token(SyntaxKind.CommaToken), + IdentifierName(genA)}))))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + IdentifierName("exception"))))))))))) + .WithSemicolonToken( + Token(SyntaxKind.SemicolonToken)); + + + // Fail :: String -> Exception -> MA + var failMethod3 = MethodDeclaration( + structA, + Identifier(failName)) + .WithModifiers( + TokenList( + new[]{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword)})) + .WithParameterList( + ParameterList( + SeparatedList( + new SyntaxNodeOrToken[]{ + Parameter( + Identifier("message")) + .WithType( + PredefinedType( + Token(SyntaxKind.StringKeyword))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("exception")) + .WithType( + IdentifierName("Exception"))}))) + .WithExpressionBody( + ArrowExpressionClause( + ObjectCreationExpression(structA) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("Prelude"), + GenericName( + Identifier("ReaderFail")) + .WithTypeArgumentList( + TypeArgumentList( + SeparatedList( + new SyntaxNodeOrToken[]{ + ParseTypeName(envType), + Token(SyntaxKind.CommaToken), + IdentifierName(genA)}))))) + .WithArgumentList( + ArgumentList( SeparatedList( new SyntaxNodeOrToken[]{ Argument( - LiteralExpression( - SyntaxKind.DefaultLiteralExpression, - Token(SyntaxKind.DefaultKeyword))), + IdentifierName("message")), Token(SyntaxKind.CommaToken), Argument( - LiteralExpression( - SyntaxKind.TrueLiteralExpression))}))))))))) + IdentifierName("exception"))}))))))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); @@ -255,7 +371,7 @@ public Task> GenerateAsync( // Run :: MA -> Env -> TryOption A var runMethod = MethodDeclaration( GenericName( - Identifier("TryOption")) + Identifier("ReaderResult")) .WithTypeArgumentList( TypeArgumentList( SingletonSeparatedList( @@ -506,7 +622,7 @@ public Task> GenerateAsync( SingletonSeparatedList( Argument( IdentifierName("env"))))), - IdentifierName("IfNoneOrFail"))) + IdentifierName("IfFail"))) .WithArgumentList( ArgumentList( SingletonSeparatedList( @@ -586,7 +702,7 @@ public Task> GenerateAsync( SingletonSeparatedList( Argument( IdentifierName("env"))))), - IdentifierName("IfNoneOrFail"))) + IdentifierName("IfFail"))) .WithArgumentList( ArgumentList( SingletonSeparatedList( @@ -667,7 +783,7 @@ public Task> GenerateAsync( SingletonSeparatedList( Argument( IdentifierName("env"))))), - IdentifierName("IfNoneOrFail"))) + IdentifierName("IfFail"))) .WithArgumentList( ArgumentList( SingletonSeparatedList( @@ -821,48 +937,6 @@ MethodDeclarationSyntax BindMethod(string name) => .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); - // [name] :: MA -> (A -> bool) -> MA - MethodDeclarationSyntax FilterMethod(string name) => - MethodDeclaration(structA, Identifier(name)) - .WithModifiers( - TokenList( - Token(SyntaxKind.PublicKeyword))) - .WithParameterList( - ParameterList( - SingletonSeparatedList( - Parameter( - Identifier("f")) - .WithType( - GenericName( - Identifier("Func")) - .WithTypeArgumentList( - TypeArgumentList( - SeparatedList( - new SyntaxNodeOrToken[]{ - IdentifierName(genA), - Token(SyntaxKind.CommaToken), - PredefinedType( - Token(SyntaxKind.BoolKeyword))}))))))) - .WithExpressionBody( - ArrowExpressionClause( - ObjectCreationExpression(structA) - .WithArgumentList( - ArgumentList( - SingletonSeparatedList( - Argument( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("__comp"), - IdentifierName("Where"))) - .WithArgumentList( - ArgumentList( - SingletonSeparatedList( - Argument( - IdentifierName("f"))))))))))) - .WithSemicolonToken( - Token(SyntaxKind.SemicolonToken)); - partialStruct = partialStruct.WithMembers( List( @@ -870,15 +944,15 @@ MethodDeclarationSyntax FilterMethod(string name) => compField, ctor, returnMethod, - failMethod, + failMethod1, + failMethod2, + failMethod3, MapMethod("Map"), MapMethod("Select"), BindMethod("Bind"), BindMethod("SelectMany"), selectManyMethod, runMethod, - FilterMethod("Filter"), - FilterMethod("Where"), doMethod, strictMethod, toSeqMethod, @@ -920,8 +994,40 @@ MethodDeclarationSyntax FilterMethod(string name) => .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); - // Fail :: Unit -> MA - var failFunc = MethodDeclaration( + // Fail :: String -> MA + var failFunc1 = MethodDeclaration( + structA, + Identifier(failName)) + .WithModifiers( + TokenList( + new[]{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword)})) + .WithTypeParameterList(applyToStruct.TypeParameterList) + .WithParameterList( + ParameterList( + SingletonSeparatedList( + Parameter( + Identifier("message")) + .WithType( + PredefinedType( + Token(SyntaxKind.StringKeyword)))))) + .WithExpressionBody( + ArrowExpressionClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + structA, + IdentifierName("Fail"))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + IdentifierName("message"))))))) + .WithSemicolonToken( + Token(SyntaxKind.SemicolonToken)); + + var failFunc2 = MethodDeclaration( structA, Identifier(failName)) .WithModifiers( @@ -930,12 +1036,68 @@ MethodDeclarationSyntax FilterMethod(string name) => Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)})) .WithTypeParameterList(applyToStruct.TypeParameterList) + .WithParameterList( + ParameterList( + SingletonSeparatedList( + Parameter( + Identifier("exception")) + .WithType( + IdentifierName("Exception"))))) .WithExpressionBody( ArrowExpressionClause( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - structA, - IdentifierName(failName)))) + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + structA, + IdentifierName("Fail"))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + IdentifierName("exception"))))))) + .WithSemicolonToken( + Token(SyntaxKind.SemicolonToken)); + + var failFunc3 = MethodDeclaration( + structA, + Identifier(failName)) + .WithModifiers( + TokenList( + new[]{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword)})) + .WithTypeParameterList(applyToStruct.TypeParameterList) + .WithParameterList( + ParameterList( + SeparatedList( + new SyntaxNodeOrToken[]{ + Parameter( + Identifier("message")) + .WithType( + PredefinedType( + Token(SyntaxKind.StringKeyword))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("exception")) + .WithType( + IdentifierName("Exception"))}))) + .WithExpressionBody( + ArrowExpressionClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + structA, + IdentifierName("Fail"))) + + .WithArgumentList( + ArgumentList( + SeparatedList( + new SyntaxNodeOrToken[]{ + Argument( + IdentifierName("message")), + Token(SyntaxKind.CommaToken), + Argument( + IdentifierName("exception"))}))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); @@ -966,32 +1128,60 @@ MethodDeclarationSyntax FilterMethod(string name) => IdentifierName(genA)}))))))) .WithExpressionBody( ArrowExpressionClause( - ObjectCreationExpression(structA) - .WithArgumentList( - ArgumentList( - SingletonSeparatedList( - Argument( - SimpleLambdaExpression( - Parameter( - Identifier("env")), - TupleExpression( - SeparatedList( - new SyntaxNodeOrToken[]{ - Argument( - InvocationExpression( - IdentifierName("f")) - .WithArgumentList( - ArgumentList( - SingletonSeparatedList( - Argument( - IdentifierName("env")))))), - Token(SyntaxKind.CommaToken), - Argument( - LiteralExpression( - SyntaxKind.FalseLiteralExpression))}))))))))) + ObjectCreationExpression(structA) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + SimpleLambdaExpression( + Parameter( + Identifier("__env")), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + GenericName( + Identifier("ReaderResult")) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + IdentifierName(genA)))), + IdentifierName("New"))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + InvocationExpression( + IdentifierName("f")) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + IdentifierName("__env")))))))))))))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); + var askInner = ObjectCreationExpression(structEnv) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + SimpleLambdaExpression( + Parameter( + Identifier("__env")), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + GenericName( + Identifier("ReaderResult")) + .WithTypeArgumentList( + TypeArgumentList(SingletonSeparatedList(ParseTypeName(envType)))), + IdentifierName("New"))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + IdentifierName("__env")))))))))); + // ask :: M Env var askFunc = applyToStruct.TypeParameterList.Parameters.Count == 1 ? FieldDeclaration( @@ -1002,23 +1192,10 @@ MethodDeclarationSyntax FilterMethod(string name) => Identifier("ask")) .WithInitializer( EqualsValueClause( - ObjectCreationExpression(structEnv) - .WithArgumentList( - ArgumentList( - SingletonSeparatedList( - Argument( - SimpleLambdaExpression( - Parameter( - Identifier("env")), - TupleExpression( - SeparatedList( - new SyntaxNodeOrToken[]{ - Argument( - IdentifierName("env")), - Token(SyntaxKind.CommaToken), - Argument( - LiteralExpression( - SyntaxKind.FalseLiteralExpression))})))))))))))) + + askInner + + ))))) .WithModifiers( TokenList( new[]{ @@ -1039,23 +1216,10 @@ MethodDeclarationSyntax FilterMethod(string name) => applyToStruct.TypeParameterList.Parameters.Take(applyToStruct.TypeParameterList.Parameters.Count - 1)))) .WithExpressionBody( ArrowExpressionClause( - ObjectCreationExpression(structEnv) - .WithArgumentList( - ArgumentList( - SingletonSeparatedList( - Argument( - SimpleLambdaExpression( - Parameter( - Identifier("env")), - TupleExpression( - SeparatedList( - new SyntaxNodeOrToken[]{ - Argument( - IdentifierName("env")), - Token(SyntaxKind.CommaToken), - Argument( - LiteralExpression( - SyntaxKind.FalseLiteralExpression))}))))))))) + + askInner + + )) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); @@ -1106,7 +1270,9 @@ MethodDeclarationSyntax FilterMethod(string name) => List( new MemberDeclarationSyntax[]{ returnFunc, - failFunc, + failFunc1, + failFunc2, + failFunc3, asksFunc, askFunc, localFunc diff --git a/LanguageExt.Core/ClassInstances/Monad/MReader.cs b/LanguageExt.Core/ClassInstances/Monad/MReader.cs index 1cb8c7cea..d46fea1c1 100644 --- a/LanguageExt.Core/ClassInstances/Monad/MReader.cs +++ b/LanguageExt.Core/ClassInstances/Monad/MReader.cs @@ -17,31 +17,33 @@ public struct MReader : public MB Bind(Reader ma, Func f) where MONADB : struct, Monad => 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(Reader ma, Func f) where MONADB : struct, MonadAsync => 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 Fail(object err = null) => - new Reader(_ => (default(A), true)); + public Reader Fail(object err = null) => _ => + err is Common.Error error ? ReaderResult.New(error) + : err is Exception exception ? ReaderResult.New(Common.Error.New(exception)) + : ReaderResult.Bottom; [Pure] - public Reader Reader(Func f) => env => - (f(env), false); + public Reader Reader(Func f) => env => + ReaderResult.New(f(env)); [Pure] public Reader Ask() => env => - (env, false); + ReaderResult.New(env); [Pure] public Reader Local(Reader ma, Func f) => env => @@ -49,7 +51,7 @@ public Reader Local(Reader ma, Func f) => env => [Pure] public Reader Return(Func f) => env => - (f(env), false); + ReaderResult.New(f(env)); [Pure] public Reader Run(Func> f) => env => @@ -58,23 +60,23 @@ public Reader Run(Func> f) => env => [Pure] public Reader Plus(Reader ma, Reader mb) => env => { - var (a, faulted) = ma(env); - return faulted + var resA = ma(env); + return resA.IsFaulted ? mb(env) - : (a, faulted); + : resA; }; [Pure] public Reader Zero() => - _ => (default(A), true); + _ => ReaderResult.Bottom; [Pure] public Func Fold(Reader fa, S state, Func 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] diff --git a/LanguageExt.Core/Common/Error.cs b/LanguageExt.Core/Common/Error.cs new file mode 100644 index 000000000..6a5be4ba2 --- /dev/null +++ b/LanguageExt.Core/Common/Error.cs @@ -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; + + Error(int code, string message, Option 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) => + 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); + } +} diff --git a/LanguageExt.Core/DataTypes/Reader/Reader.Extensions.cs b/LanguageExt.Core/DataTypes/Reader/Reader.Extensions.cs index 90b3a7399..4edc200dc 100644 --- a/LanguageExt.Core/DataTypes/Reader/Reader.Extensions.cs +++ b/LanguageExt.Core/DataTypes/Reader/Reader.Extensions.cs @@ -4,6 +4,7 @@ using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; +using System.Collections.Generic; /// /// Reader monad extensions @@ -21,25 +22,17 @@ public static Reader Flatten(this Reader> ma /// Runs the Reader monad and memoizes the result in a TryOption monad. Use /// Match, IfSucc, IfNone, etc to extract. /// - public static TryOption Run(this Reader self, Env env) + public static ReaderResult Run(this Reader self, Env env) { try { - if (self == null) return () => Option.None; ; - if (env == null) return () => Option.None; ; - var (a, b) = self(env); - if(b) - { - return () => Option.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(e); + return ReaderResult.New(Error.New(e)); } } @@ -49,10 +42,58 @@ public static Reader> AsEnumerable(this Reader self) [Pure] public static Seq ToSeq(this Reader self, Env env) => - self.Map(x => x.Cons()).Run(env).IfNoneOrFail(Empty); + self.Run(env).ToSeq(); [Pure] - public static Seq AsEnumerable(this Reader self, Env env) => + public static Lst ToList(this Reader self, Env env) => + self.Run(env).ToList(); + + [Pure] + public static Option ToOption(this Reader self, Env env) => + self.Run(env).ToOption(); + + [Pure] + public static OptionUnsafe ToOptionUnsafe(this Reader self, Env env) => + self.Run(env).ToOptionUnsafe(); + + [Pure] + public static OptionAsync ToOptionAsync(this Reader self, Env env) => + self.Run(env).ToOptionAsync(); + + [Pure] + public static Either ToEither(this Reader self, Env env) => + self.Run(env).ToEither(); + + [Pure] + public static Either ToEither(this Reader self, Env env, Func Left) => + self.Run(env).ToEither(Left); + + [Pure] + public static EitherUnsafe ToEitherUnsafe(this Reader self, Env env) => + self.Run(env).ToEitherUnsafe(); + + [Pure] + public static EitherUnsafe ToEitherUnsafe(this Reader self, Env env, Func Left) => + self.Run(env).ToEitherUnsafe(Left); + + [Pure] + public static EitherAsync ToEitherAsync(this Reader self, Env env) => + self.Run(env).ToEitherAsync(); + + [Pure] + public static EitherAsync ToEitherAsync(this Reader self, Env env, Func Left) => + self.Run(env).ToEitherAsync(Left); + + [Pure] + public static Try ToTry(this Reader self, Env env) => + self.Run(env).ToTry(); + + [Pure] + public static TryAsync ToTryAsync(this Reader self, Env env) => + self.Run(env).ToTryAsync(); + + [Pure] + public static IEnumerable AsEnumerable(this Reader self, Env env) => ToSeq(self, env); public static Reader Iter(this Reader self, Action action) => @@ -82,26 +123,14 @@ public static Reader Fold(this Reader self, S state, public static Reader Map(this Reader self, Func mapper) => self.Select(mapper); - [Pure] - public static Reader Filter(this Reader self, Func pred) => - self.Where(pred); - - [Pure] - public static Reader Where(this Reader self, Func pred) => env => - { - var (a, faulted) = self(env); - if (faulted || !pred(a)) return (a, true); - return (a, false); - }; - /// /// Force evaluation of the monad (once only) /// [Pure] public static Reader Strict(this Reader ma) { - Option<(A, bool)> cache = default; - object sync = new object(); + Option> cache = default; + var sync = new object(); return env => { if (cache.IsSome) return cache.Value; @@ -160,10 +189,10 @@ public static Reader SelectMany( public static Reader Fold(this Reader self, Func f) => env => { - var (x, b) = self(env); - return b - ? (default(Env), true) - : (f(env, x), false); + var resA = self(env); + return resA.IsFaulted + ? ReaderResult.New(resA.ErrorInt) + : ReaderResult.New(f(env, resA.Value)); }; } diff --git a/LanguageExt.Core/DataTypes/Reader/Reader.Prelude.cs b/LanguageExt.Core/DataTypes/Reader/Reader.Prelude.cs index ad95ffaec..2154280d2 100644 --- a/LanguageExt.Core/DataTypes/Reader/Reader.Prelude.cs +++ b/LanguageExt.Core/DataTypes/Reader/Reader.Prelude.cs @@ -36,6 +36,34 @@ public static Reader Reader(A value) => public static Reader Reader(Func f) => default(MReader).Return(f); + /// + /// Reader failure + /// + [Pure] + public static Reader ReaderFail(string error) => env => + ReaderResult.New(Common.Error.New(error)); + + /// + /// Reader failure + /// + [Pure] + public static Reader ReaderFail(int code, string error) => env => + ReaderResult.New(Common.Error.New(code, error)); + + /// + /// Reader failure + /// + [Pure] + public static Reader ReaderFail(string error, Exception exception) => env => + ReaderResult.New(Common.Error.New(error, exception)); + + /// + /// Reader failure + /// + [Pure] + public static Reader ReaderFail(Exception exception) => env => + ReaderResult.New(Common.Error.New(exception)); + /// /// Retrieves the reader monad environment. /// @@ -74,13 +102,13 @@ public static Reader> choose(params Reader { 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>.Bottom; }; /// @@ -94,9 +122,9 @@ public static Reader tryread(Reader m) => { return m(state); } - catch + catch(Exception e) { - return (default(A), true); + return ReaderResult.New(Common.Error.New(e)); } }; diff --git a/LanguageExt.Core/DataTypes/Reader/Reader.cs b/LanguageExt.Core/DataTypes/Reader/Reader.cs index 925c06118..2214c223f 100644 --- a/LanguageExt.Core/DataTypes/Reader/Reader.cs +++ b/LanguageExt.Core/DataTypes/Reader/Reader.cs @@ -1,7 +1,122 @@ using System; using System.Diagnostics.Contracts; +using LanguageExt.Common; +using static LanguageExt.Prelude; namespace LanguageExt { - public delegate (A Value, bool IsFaulted) Reader(Env env); -} \ No newline at end of file + public delegate ReaderResult Reader(Env env); + + public struct ReaderResult + { + public static readonly ReaderResult Bottom = new ReaderResult(); + + enum State + { + Bottom, + Top + } + + internal readonly A Value; + readonly Option Error; + State Init; + + ReaderResult(A value, Option error) + { + Value = value; + Error = error; + Init = State.Top; + } + + internal Error ErrorInt => Init == State.Bottom + ? Common.Error.Bottom + : (Error)Error; + + public bool IsFaulted => + Init == State.Bottom || Error.IsSome; + + public static ReaderResult New(A value) => + new ReaderResult(value, None); + + public static ReaderResult New(Error error) => + new ReaderResult(default, Some(error)); + + internal static ReaderResult New(Option error) => + new ReaderResult(default, error); + + public B Match(Func Succ, Func Fail) => + IsFaulted + ? Succ(Value) + : Fail(ErrorInt); + + public A IfFail(A value) => + IsFaulted + ? value + : Value; + + public Option ToOption() => + IsFaulted + ? None + : Some(Value); + + public OptionUnsafe ToOptionUnsafe() => + IsFaulted + ? None + : SomeUnsafe(Value); + + public OptionAsync ToOptionAsync() => + IsFaulted + ? OptionAsync.None + : SomeAsync(Value); + + public Seq ToSeq() => + IsFaulted + ? Empty + : Seq1(Value); + + public Lst ToList() => + IsFaulted + ? Empty + : List(Value); + + public Either ToEither() => + IsFaulted + ? Left(ErrorInt) + : Right(Value); + + public Either ToEither(Func Left) => + IsFaulted + ? Left(Left(ErrorInt)) + : Right(Value); + + public EitherUnsafe ToEitherUnsafe() => + IsFaulted + ? LeftUnsafe(ErrorInt) + : RightUnsafe(Value); + + public EitherUnsafe ToEitherUnsafe(Func Left) => + IsFaulted + ? LeftUnsafe(Left(ErrorInt)) + : RightUnsafe(Value); + + public EitherAsync ToEitherAsync() => + IsFaulted + ? LeftAsync(ErrorInt) + : RightAsync(Value); + + public EitherAsync ToEitherAsync(Func Left) => + IsFaulted + ? LeftAsync(Left(ErrorInt)) + : RightAsync(Value); + + public Try ToTry() => + IsFaulted + ? Try((ErrorInt).ToException()) + : Try(Value); + + public TryAsync ToTryAsync() => + IsFaulted + ? TryAsync((ErrorInt).ToException()) + : TryAsync(Value); + } +} diff --git a/LanguageExt.Core/DataTypes/Reader/ReaderResult.cs b/LanguageExt.Core/DataTypes/Reader/ReaderResult.cs index 877d037d5..346380bc7 100644 --- a/LanguageExt.Core/DataTypes/Reader/ReaderResult.cs +++ b/LanguageExt.Core/DataTypes/Reader/ReaderResult.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Text; +//using System; +//using System.Collections.Generic; +//using System.Text; -namespace LanguageExt -{ - /// - /// Convenience methods for returning from a Reader computation - /// - public static class ReaderResult - { - public static (A Value, Env Environment, bool IsBottom) ToReader(this (A, Env) self) => - self.Add(false); +//namespace LanguageExt +//{ +// /// +// /// Convenience methods for returning from a Reader computation +// /// +// public static class ReaderResult +// { +// public static (A Value, Env Environment, bool IsBottom) ToReader(this (A, Env) self) => +// self.Add(false); - public static (A Value, Env Environment, bool IsBottom) Return(A value, Env env) => - (value, env, false); +// public static (A Value, Env Environment, bool IsBottom) Return(A value, Env env) => +// (value, env, false); - public static (A Value, Env Environment, bool IsBottom) Fail() => - (default(A), default(Env), true); +// public static (A Value, Env Environment, bool IsBottom) Fail() => +// (default(A), default(Env), true); - public static (Unit Value, Env Environment, bool IsBottom) Fail() => - (default(Unit), default(Env), true); - } -} +// public static (Unit Value, Env Environment, bool IsBottom) Fail() => +// (default(Unit), default(Env), true); +// } +//} diff --git a/LanguageExt.Core/HKT/Extensions/TraverseExtensions.cs b/LanguageExt.Core/HKT/Extensions/TraverseExtensions.cs index 1c98f8588..cf77d8280 100644 --- a/LanguageExt.Core/HKT/Extensions/TraverseExtensions.cs +++ b/LanguageExt.Core/HKT/Extensions/TraverseExtensions.cs @@ -93,11 +93,11 @@ internal static Reader> SequenceFast(this IEnumerable(); foreach (var item in ma) { - var (a, bottom) = item(env); - if (bottom) return (Value: new List(), IsFaulted: true); - values.Add(a); + var resA = item(env); + if (resA.IsFaulted) return ReaderResult>.New(resA.ErrorInt); + values.Add(resA.Value); } - return (Value: values, IsFaulted: false); + return ReaderResult>.New(values); }; internal static Reader> TraverseFast(this IEnumerable> ma, Func f) => env => @@ -105,11 +105,11 @@ internal static Reader> TraverseFast(this IEnumerable(); foreach (var item in ma) { - var (a, bottom) = item(env); - if (bottom) return (Value: new List(), IsFaulted: true); - values.Add(f(a)); + var resA = item(env); + if (resA.IsFaulted) return ReaderResult>.New(resA.ErrorInt); + values.Add(f(resA.Value)); } - return (Value: values, IsFaulted: false); + return ReaderResult>.New(values); }; public static Reader> Sequence(this Seq> ma) => diff --git a/Samples/TestBed/TestCodeGen.cs b/Samples/TestBed/TestCodeGen.cs index c2382edfc..166d7966a 100644 --- a/Samples/TestBed/TestCodeGen.cs +++ b/Samples/TestBed/TestCodeGen.cs @@ -54,7 +54,7 @@ from ls in Subsystem.ReadAllLines("c:/test.txt") from _ in Subsystem.WriteAllLines("c:/test-copy.txt", ls) select ls.Count; - var res = comp.Run(new RealIO()).IfNoneOrFail(0); + var res = comp.Run(new RealIO()).IfFail(0); } }