├── Monacs.UnitTests ├── Program.fs ├── Monacs.UnitTests.fsproj ├── ErrorDetailsTests.fs ├── ResultTests.Tuple.fs ├── OptionTests.fs ├── ResultTests.fs └── ResultTests.Async.fs ├── .editorconfig ├── .travis.yml ├── .vscode └── tasks.json ├── Monacs.Core ├── Monacs.Core.csproj ├── Result.Extensions.Unit.cs ├── Monacs.Core.nuspec ├── Unit │ └── Unit.cs ├── ErrorDetails.cs ├── Errors.cs ├── Option.cs ├── Result.cs ├── Result.Extensions.Tuple.cs ├── Option.Extensions.cs ├── Result.Extensions.Async.cs └── Result.Extensions.cs ├── LICENSE ├── Monacs.sln ├── README.md └── .gitignore /Monacs.UnitTests/Program.fs: -------------------------------------------------------------------------------- 1 | module Program = let [] main _ = 0 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = false -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | mono: latest 3 | dotnet: 2.1.4 4 | dist: trusty 5 | env: FrameworkPathOverride=/usr/lib/mono/4.5/ 6 | script: 7 | - dotnet build -c Release 8 | - dotnet test -c Release --no-build Monacs.UnitTests/Monacs.UnitTests.fsproj 9 | deploy: 10 | provider: script 11 | skip_cleanup: true 12 | script: 13 | - cd Monacs.Core && dotnet pack Monacs.Core.csproj -c Release -o . -v n && dotnet nuget push Monacs.Core.*.nupkg -k $NUGET_API_KEY -s https://www.nuget.org/api/v2/package 14 | on: 15 | branch: master 16 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet build", 7 | "type": "shell", 8 | "group": { 9 | "kind": "build", 10 | "isDefault": true 11 | }, 12 | "presentation": { 13 | "reveal": "silent" 14 | }, 15 | "problemMatcher": "$msCompile" 16 | }, 17 | { 18 | "label": "test", 19 | "command": "dotnet test ./Monacs.UnitTests/Monacs.UnitTests.fsproj", 20 | "type": "shell", 21 | "group": { 22 | "kind": "test", 23 | "isDefault": true 24 | }, 25 | "presentation": { 26 | "reveal": "always" 27 | }, 28 | "problemMatcher": "$msCompile" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /Monacs.Core/Monacs.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0;netstandard1.3;net461 4 | $(MSBuildThisFileDirectory)$(MSBuildProjectName).nuspec 5 | latest 6 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 7 | PackageReference 8 | true 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Bartosz Sokół 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Monacs.UnitTests/Monacs.UnitTests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.0 4 | false 5 | true 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Monacs.Core/Result.Extensions.Unit.cs: -------------------------------------------------------------------------------- 1 | namespace Monacs.Core 2 | { 3 | /// 4 | /// Contains the set of extensions to work with the type. 5 | /// 6 | public static class UnitResult 7 | { 8 | /// 9 | /// Creates the Ok case instance of the . 10 | /// 11 | public static Result Ok() => 12 | Result.Ok(Unit.Unit.Default); 13 | 14 | /// 15 | /// Creates the Error case instance of the type, containing error instead of value. 16 | /// 17 | /// Details of the error. 18 | public static Result Error(ErrorDetails error) => 19 | Result.Error(error); 20 | 21 | /// 22 | /// Rejects the value of the and returns instead. 23 | /// If the input is Error then the error details are preserved. 24 | /// 25 | /// Type of the encapsulated value. 26 | /// The result of which the value should be ignored. 27 | public static Result Ignore(this Result result) => 28 | result.Map(_ => Unit.Unit.Default); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Monacs.Core/Monacs.Core.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Monacs.Core 5 | 0.3.0 6 | Bartosz Sokół 7 | Bartosz Sokół 8 | https://github.com/bartsokol/Monacs/blob/master/LICENSE 9 | https://github.com/bartsokol/Monacs 10 | false 11 | Collection of basic monads and functional extensions for C# 12 | csharp functional functional-programming monads railway-oriented-programming rop 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Monacs.Core/Unit/Unit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Monacs.Core.Unit 4 | { 5 | /// 6 | /// Type that has only one value. 7 | /// Used to replace void whenever some value is needed, e.g. you can return Task{Unit}. 8 | /// 9 | public readonly struct Unit : IEquatable 10 | { 11 | private static Unit _default = default; 12 | 13 | /// 14 | /// The only value of . 15 | /// 16 | public static ref readonly Unit Default => ref _default; 17 | 18 | /// 19 | /// is always equal to itself. There is only one possible value of . 20 | /// 21 | public bool Equals(Unit other) => true; 22 | 23 | /// 24 | /// is only equal to itself. 25 | /// 26 | public override bool Equals(object obj) => obj is Unit; 27 | 28 | /// 29 | /// Hash Code of is always 0. 30 | /// 31 | public override int GetHashCode() => 0; 32 | 33 | /// 34 | /// String representation of is (). 35 | /// 36 | public override string ToString() => "()"; 37 | 38 | /// 39 | /// is always equal to itself. 40 | /// 41 | public static bool operator ==(in Unit first, in Unit second) => true; 42 | 43 | /// 44 | /// is always equal to itself. 45 | /// 46 | public static bool operator !=(in Unit first, in Unit second) => false; 47 | } 48 | } -------------------------------------------------------------------------------- /Monacs.Core/ErrorDetails.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Monacs.Core 4 | { 5 | /// 6 | /// Represents the details of the error in case of failed operation. 7 | /// To create the instances use the factory methods from the class. 8 | /// 9 | public readonly struct ErrorDetails 10 | { 11 | internal ErrorDetails(in ErrorLevel level, in Option message, in Option key, in Option exception, in Option metadata) 12 | { 13 | Level = level; 14 | Message = message; 15 | Key = key; 16 | Exception = exception; 17 | Metadata = metadata; 18 | } 19 | 20 | /// 21 | /// Contains the error severity described by . 22 | /// 23 | public ErrorLevel Level { get; } 24 | 25 | /// 26 | /// Contains optional message to describe the error details. 27 | /// 28 | public Option Message { get; } 29 | 30 | /// 31 | /// Contains optional error key to identify the error. 32 | /// 33 | public Option Key { get; } 34 | 35 | /// 36 | /// Contains optional exception which operation ended up with. 37 | /// Set to None if no exception occured. 38 | /// 39 | public Option Exception { get; } 40 | 41 | /// 42 | /// Contains optional error metadata. 43 | /// 44 | public Option Metadata { get; } 45 | } 46 | 47 | /// 48 | /// Represents the severity of the error. 49 | /// 50 | public enum ErrorLevel 51 | { 52 | Trace = 0, 53 | Debug = 1, 54 | Info = 2, 55 | Warn = 3, 56 | Error = 4, 57 | Fatal = 5 58 | } 59 | } -------------------------------------------------------------------------------- /Monacs.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.26730.16 4 | MinimumVisualStudioVersion = 15.0.26124.0 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monacs.Core", "Monacs.Core/Monacs.Core.csproj", "{AC132BCD-1473-4365-8A14-6C2A0EB41EBF}" 6 | EndProject 7 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Monacs.UnitTests", "Monacs.UnitTests/Monacs.UnitTests.fsproj", "{355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}" 8 | EndProject 9 | Global 10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 11 | Debug|Any CPU = Debug|Any CPU 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|Any CPU = Release|Any CPU 15 | Release|x64 = Release|x64 16 | Release|x86 = Release|x86 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Debug|x64.ActiveCfg = Debug|Any CPU 22 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Debug|x64.Build.0 = Debug|Any CPU 23 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Debug|x86.ActiveCfg = Debug|Any CPU 24 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Debug|x86.Build.0 = Debug|Any CPU 25 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Release|x64.ActiveCfg = Release|Any CPU 28 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Release|x64.Build.0 = Release|Any CPU 29 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Release|x86.ActiveCfg = Release|Any CPU 30 | {AC132BCD-1473-4365-8A14-6C2A0EB41EBF}.Release|x86.Build.0 = Release|Any CPU 31 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Debug|x64.ActiveCfg = Debug|Any CPU 34 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Debug|x64.Build.0 = Debug|Any CPU 35 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Debug|x86.ActiveCfg = Debug|Any CPU 36 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Debug|x86.Build.0 = Debug|Any CPU 37 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Release|x64.ActiveCfg = Release|Any CPU 40 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Release|x64.Build.0 = Release|Any CPU 41 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Release|x86.ActiveCfg = Release|Any CPU 42 | {355D8F6F-F9C8-49BC-9416-4FEA3C2F8EAF}.Release|x86.Build.0 = Release|Any CPU 43 | EndGlobalSection 44 | GlobalSection(SolutionProperties) = preSolution 45 | HideSolutionNode = FALSE 46 | EndGlobalSection 47 | GlobalSection(ExtensibilityGlobals) = postSolution 48 | SolutionGuid = {DFC93EE9-AC1E-42EC-9BEC-E68F943F2346} 49 | EndGlobalSection 50 | EndGlobal 51 | -------------------------------------------------------------------------------- /Monacs.UnitTests/ErrorDetailsTests.fs: -------------------------------------------------------------------------------- 1 | namespace Monacs.UnitTests.ErrorDetailsTests 2 | 3 | open System 4 | open Xunit 5 | open FsUnit.Xunit 6 | 7 | open Monacs.Core 8 | 9 | module Constructors = 10 | 11 | [] 12 | let ``Trace sets ErrorLevel.Trace and error details`` () = 13 | let message = "Message" 14 | let key = "key" 15 | let ex = Exception() 16 | let details = Errors.Trace(message, key, ex) 17 | details.Level |> should equal ErrorLevel.Trace 18 | details.Message |> should equal (Option.Some(message)) 19 | details.Key |> should equal (Option.Some(key)) 20 | details.Exception |> should equal (Option.Some(ex)) 21 | 22 | [] 23 | let ``Debug sets ErrorLevel.Debug and error details`` () = 24 | let message = "Message" 25 | let key = "key" 26 | let ex = Exception() 27 | let details = Errors.Debug(message, key, ex) 28 | details.Level |> should equal ErrorLevel.Debug 29 | details.Message |> should equal (Option.Some(message)) 30 | details.Key |> should equal (Option.Some(key)) 31 | details.Exception |> should equal (Option.Some(ex)) 32 | 33 | [] 34 | let ``Info sets ErrorLevel.Info and error details`` () = 35 | let message = "Message" 36 | let key = "key" 37 | let ex = Exception() 38 | let details = Errors.Info(message, key, ex) 39 | details.Level |> should equal ErrorLevel.Info 40 | details.Message |> should equal (Option.Some(message)) 41 | details.Key |> should equal (Option.Some(key)) 42 | details.Exception |> should equal (Option.Some(ex)) 43 | 44 | [] 45 | let ``Warn sets ErrorLevel.Warn and error details`` () = 46 | let message = "Message" 47 | let key = "key" 48 | let ex = Exception() 49 | let details = Errors.Warn(message, key, ex) 50 | details.Level |> should equal ErrorLevel.Warn 51 | details.Message |> should equal (Option.Some(message)) 52 | details.Key |> should equal (Option.Some(key)) 53 | details.Exception |> should equal (Option.Some(ex)) 54 | 55 | [] 56 | let ``Error sets ErrorLevel.Error and error details`` () = 57 | let message = "Message" 58 | let key = "key" 59 | let ex = Exception() 60 | let details = Errors.Error(message, key, ex) 61 | details.Level |> should equal ErrorLevel.Error 62 | details.Message |> should equal (Option.Some(message)) 63 | details.Key |> should equal (Option.Some(key)) 64 | details.Exception |> should equal (Option.Some(ex)) 65 | 66 | [] 67 | let ``Fatal sets ErrorLevel.Fatal and error details`` () = 68 | let message = "Message" 69 | let key = "key" 70 | let ex = Exception() 71 | let details = Errors.Fatal(message, key, ex) 72 | details.Level |> should equal ErrorLevel.Fatal 73 | details.Message |> should equal (Option.Some(message)) 74 | details.Key |> should equal (Option.Some(key)) 75 | details.Exception |> should equal (Option.Some(ex)) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Monacs - Collection of basic monads and functional extensions for C# 2 | 3 | This library provides few core types and functions that can help you start writing C# code in more functional way. 4 | It also encourages use of [Railway Oriented Programming](https://fsharpforfunandprofit.com/rop/) approach. 5 | 6 | Monacs uses type and function names similar to F#. That's intentional and it should make potential transition to F# easier. 7 | Some people may prefer Haskell type names (e.g. Maybe instead of Option) or LINQ-like function naming (e.g. Select instead of Map) - if that's your preference, you can always fork this library and change the names accordingly :) 8 | 9 | [![Build Status](https://travis-ci.org/MonacsLib/Monacs.svg?branch=master)](https://travis-ci.org/MonacsLib/Monacs) 10 | 11 | ## Target platform and language versions 12 | 13 | Currently the library is build against .NET 4.6.1, .NET Standard 1.3 and .NET Standard 2.0. To use the library you need to have .NET 4.6.1+ or .NET Core 1.0+ project with C# language version 6 or higher. 14 | 15 | ## Documentation 16 | 17 | You can find [docs and guides here](https://monacslib.github.io). There is also a sample project being developed using this library - [you can find it here](https://github.com/MonacsLib/Monacs.Sample). 18 | 19 | ## Want more? 20 | 21 | If you're into functional programming and you're stuck in the .NET platform (or just like it) then you should definitely go with [F# programming language](http://fsharp.org/)! 22 | Not only most of what is provided by Monacs library is already there, but you also have much better defaults in the language (immutability anyone?). 23 | There are also great libraries to go with Railway Oriented Programming like [Chessie](http://fsprojects.github.io/Chessie/), so be sure to check it out. 24 | 25 | If you really need to stick to C# and this library is not enough then you can try much more comprehensive library [Language Extensions](https://github.com/louthy/language-ext) - it probably has everything which this library covers and much, much more. 26 | 27 | ## Status 28 | 29 | It's currently work-in-progress, expect new things to be added, APIs to be changed and docs to be updated more or less often. First publicly available version was released [on NuGet](https://www.nuget.org/packages/Monacs.Core/). 30 | 31 | ## Contributing 32 | 33 | If you've found any errors or missing functionalities in the provided library feel free to report an issue using GitHub. 34 | You can also contribute by sending pull requests with bug fixes or enhancements. 35 | 36 | The [`develop` branch](https://github.com/MonacsLib/Monacs/tree/develop) should be used as a base for contributions, and I recommend rebasing on newest version of this branch when creating pull request. If you'd like to help but don't know where to look, start with [issues marked as up-for-grabs](https://github.com/MonacsLib/Monacs/labels/up-for-grabs). 37 | 38 | ## License 39 | 40 | This code is provided as is, without any warranties, as specified in [MIT license](LICENSE). 41 | 42 | ## Maintainers 43 | 44 | * Bart Sokol - [@bartsokol on GitHub](https://github.com/bartsokol/) and [@bartsokol on Twitter](https://twitter.com/bartsokol). 45 | -------------------------------------------------------------------------------- /Monacs.Core/Errors.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Monacs.Core 4 | { 5 | /// 6 | /// Contains factory methods to create instances of in a more convenient way. 7 | /// 8 | public static class Errors 9 | { 10 | /// 11 | /// Creates with level . 12 | /// 13 | /// Optional error message 14 | /// Optional key to identify the error 15 | /// Optional exception which caused the error 16 | /// Optional error metadata 17 | public static ErrorDetails Trace(string message = null, string key = null, Exception exception = null, object metadata = null) => 18 | new ErrorDetails(ErrorLevel.Trace, message.ToOption(), key.ToOption(), exception.ToOption(), metadata.ToOption()); 19 | 20 | /// 21 | /// Creates with level . 22 | /// 23 | /// Optional error message 24 | /// Optional key to identify the error 25 | /// Optional exception which caused the error 26 | /// Optional error metadata 27 | public static ErrorDetails Debug(string message = null, string key = null, Exception exception = null, object metadata = null) => 28 | new ErrorDetails(ErrorLevel.Debug, message.ToOption(), key.ToOption(), exception.ToOption(), metadata.ToOption()); 29 | 30 | /// 31 | /// Creates with level . 32 | /// 33 | /// Optional error message 34 | /// Optional key to identify the error 35 | /// Optional exception which caused the error 36 | /// Optional error metadata 37 | public static ErrorDetails Info(string message = null, string key = null, Exception exception = null, object metadata = null) => 38 | new ErrorDetails(ErrorLevel.Info, message.ToOption(), key.ToOption(), exception.ToOption(), metadata.ToOption()); 39 | 40 | /// 41 | /// Creates with level . 42 | /// 43 | /// Optional error message 44 | /// Optional key to identify the error 45 | /// Optional exception which caused the error 46 | /// Optional error metadata 47 | public static ErrorDetails Warn(string message = null, string key = null, Exception exception = null, object metadata = null) => 48 | new ErrorDetails(ErrorLevel.Warn, message.ToOption(), key.ToOption(), exception.ToOption(), metadata.ToOption()); 49 | 50 | /// 51 | /// Creates with level . 52 | /// 53 | /// Optional error message 54 | /// Optional key to identify the error 55 | /// Optional exception which caused the error 56 | /// Optional error metadata 57 | public static ErrorDetails Error(string message = null, string key = null, Exception exception = null, object metadata = null) => 58 | new ErrorDetails(ErrorLevel.Error, message.ToOption(), key.ToOption(), exception.ToOption(), metadata.ToOption()); 59 | 60 | /// 61 | /// Creates with level . 62 | /// 63 | /// Optional error message 64 | /// Optional key to identify the error 65 | /// Optional exception which caused the error 66 | /// Optional error metadata 67 | public static ErrorDetails Fatal(string message = null, string key = null, Exception exception = null, object metadata = null) => 68 | new ErrorDetails(ErrorLevel.Fatal, message.ToOption(), key.ToOption(), exception.ToOption(), metadata.ToOption()); 69 | } 70 | } -------------------------------------------------------------------------------- /Monacs.Core/Option.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Monacs.Core 5 | { 6 | /// 7 | /// Encapsulates optional value. 8 | /// It is recommended to use provided extension methods and not to use properties of the directly. 9 | /// The Option can contain a value of a type and it's called Some in such case. 10 | /// If no value is encapsulated it's called None. 11 | /// 12 | /// Type of encapsulated value. 13 | public readonly struct Option : IEquatable>, IEquatable 14 | { 15 | internal Option(T value) 16 | { 17 | Value = value; 18 | IsSome = true; 19 | } 20 | 21 | /// 22 | /// Encapsulated value. Will be default(T) in None case. 23 | /// It is not recommended to use it directly. 24 | /// Use extension methods to access the value instead, like: 25 | /// * , 26 | /// * , 27 | /// * , 28 | /// * 29 | /// 30 | public T Value { get; } 31 | 32 | /// 33 | /// Returns true if the option has value (is Some case). 34 | /// 35 | public bool IsSome { get; } 36 | 37 | /// 38 | /// Returns true if the option has no value (is None case). 39 | /// 40 | public bool IsNone => !IsSome; 41 | 42 | /// 43 | /// Creates a string representation of the . 44 | /// 45 | public override string ToString() => 46 | IsSome 47 | ? $"Some({Value})" 48 | : $"None<{typeof(T).Name}>"; 49 | 50 | /// 51 | /// Compares two instances of the . 52 | /// Two options are equal if both are of the same type, the same case 53 | /// and the underlying values are equal. 54 | /// 55 | /// Option to compare with 56 | public bool Equals(Option other) => 57 | (IsNone && other.IsNone) || (IsSome && other.IsSome && EqualityComparer.Default.Equals(Value, other.Value)); 58 | 59 | /// 60 | /// Compares with the value of type . 61 | /// Option is equal to the value of the underlying type if it's Some case 62 | /// and encapsulated value is equal to value. 63 | /// 64 | /// Option to compare with 65 | public bool Equals(T other) => 66 | IsSome && EqualityComparer.Default.Equals(Value, other); 67 | 68 | /// 69 | /// Compares the with other object. 70 | /// Option is only equal to other option given the conditions described in . 71 | /// 72 | /// Object to compare with 73 | public override bool Equals(object obj) => 74 | obj is Option && Equals((Option)obj); 75 | 76 | /// 77 | /// Computes the hashcode for the instance. 78 | /// 79 | public override int GetHashCode() 80 | { 81 | unchecked 82 | { 83 | return IsNone 84 | ? base.GetHashCode() ^ 13 85 | : Value == null ? base.GetHashCode() ^ 29 : Value.GetHashCode() ^ 31; 86 | } 87 | } 88 | 89 | public static bool operator ==(in Option a, in Option b) => a.Equals(b); 90 | 91 | public static bool operator !=(in Option a, in Option b) => !a.Equals(b); 92 | 93 | public static bool operator ==(in Option a, in T b) => a.Equals(b); 94 | 95 | public static bool operator !=(in Option a, in T b) => !a.Equals(b); 96 | 97 | public static bool operator ==(in T a, in Option b) => b.Equals(a); 98 | 99 | public static bool operator !=(in T a, in Option b) => !b.Equals(a); 100 | 101 | public static implicit operator T(in Option option) => option.IsSome ? option.Value : default; 102 | } 103 | } -------------------------------------------------------------------------------- /Monacs.Core/Result.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Monacs.Core 5 | { 6 | /// 7 | /// Represents the result of the operation that may succeed or fail. 8 | /// It is recommended to use provided extension methods and not to use properties of the directly. 9 | /// If the operation succeeded it will contain a value of a type and it's called Ok in such case. 10 | /// If the operation failed it will contain error information of type and it's called Error. 11 | /// 12 | /// Expected return value type. 13 | public readonly struct Result : IEquatable> 14 | { 15 | internal Result(T value) 16 | { 17 | Value = value; 18 | Error = default(ErrorDetails); 19 | IsOk = true; 20 | } 21 | 22 | internal Result(in ErrorDetails error) 23 | { 24 | Value = default; 25 | Error = error; 26 | IsOk = false; 27 | } 28 | 29 | /// 30 | /// Contains the computed value of the operation if it ends successfully. 31 | /// It is not recommended to use it directly. 32 | /// Use extension methods to access the value instead, like: 33 | /// * , 34 | /// * , 35 | /// * , 36 | /// * 37 | /// 38 | public T Value { get; } 39 | 40 | /// 41 | /// Contains error details when operation ended up with failure. 42 | /// It is not recommended to use it directly. 43 | /// Use extension methods to access the value instead, like: 44 | /// * 45 | /// * 46 | /// 47 | public ErrorDetails Error { get; } 48 | 49 | /// 50 | /// Indicates that the Result is on the success path (Ok case). 51 | /// You should expect the output in the field. 52 | /// 53 | public bool IsOk { get; } 54 | 55 | /// 56 | /// Indicates that the result is on the failure path (Error case). 57 | /// You should expect error in field 58 | /// and no value in the field. 59 | /// 60 | public bool IsError => !IsOk; 61 | 62 | /// 63 | /// Creates a string representation of the . 64 | /// 65 | public override string ToString() => 66 | IsOk 67 | ? $"Ok<{typeof(T).Name}>({Value})" 68 | : $"Error<{typeof(T).Name}>({Error})"; 69 | 70 | /* Equality */ 71 | 72 | /// 73 | /// Compares two instances of the . 74 | /// Two results are equal if both are of the same type, the same case 75 | /// and the underlying values (of errors) are equal. 76 | /// 77 | /// Result to compare with 78 | public bool Equals(Result other) => 79 | (IsError && other.IsError && EqualityComparer.Default.Equals(Error, other.Error)) 80 | || (IsOk && other.IsOk && EqualityComparer.Default.Equals(Value, other.Value)); 81 | 82 | /// 83 | /// Compares the with other object. 84 | /// Result is only equal to other result given the conditions described in . 85 | /// 86 | /// Object to compare with 87 | public override bool Equals(object obj) => 88 | obj is Result && Equals((Result)obj); 89 | 90 | /// 91 | /// Computes the hashcode for the instance. 92 | /// 93 | public override int GetHashCode() 94 | { 95 | unchecked 96 | { 97 | return IsError 98 | ? Error.GetHashCode() ^ 13 99 | : Value == null ? base.GetHashCode() ^ 29 : Value.GetHashCode() ^ 31; 100 | } 101 | } 102 | 103 | public static bool operator ==(in Result a, in Result b) => 104 | a.Equals(b); 105 | 106 | public static bool operator !=(in Result a, in Result b) => 107 | !a.Equals(b); 108 | } 109 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | *.orig 255 | -------------------------------------------------------------------------------- /Monacs.UnitTests/ResultTests.Tuple.fs: -------------------------------------------------------------------------------- 1 | namespace Monacs.UnitTests.ResultTests.Tuple 2 | 3 | open System 4 | open Xunit 5 | open FsUnit.Xunit 6 | 7 | open Monacs.Core 8 | 9 | module Map2 = 10 | 11 | let testTuple = struct ("Meaning of Life", 42) 12 | let errorMessage = "Some error message." 13 | 14 | [] 15 | let ``Map2 returns result of mapper when value is Ok<(TFst, TSnd)>`` () = 16 | let value = Result.Ok(testTuple) 17 | let expected = Result.Ok(testTuple.ToString()) 18 | TupleResult.Map2(value, (fun x y -> (x, y).ToString())) |> should equal expected 19 | 20 | [] 21 | let ``Map2 returns Error when value is Error<(TFst, TSnd)>`` () = 22 | let error = Errors.Error(errorMessage) 23 | let value = Result.Error(error) 24 | let expected = Result.Error(error) 25 | TupleResult.Map2(value, (fun _ _ -> "")) |> should equal expected 26 | 27 | module Map3 = 28 | 29 | let testTuple = struct ("Meaning of Life", 42, 0.1) 30 | let errorMessage = "Some error message." 31 | 32 | [] 33 | let ``Map3 returns result of mapper when value is Ok<(TFst, TSnd, TTrd)>`` () = 34 | let value = Result.Ok(testTuple) 35 | let expected = Result.Ok(testTuple.ToString()) 36 | TupleResult.Map3(value, (fun x y z -> (x, y, z).ToString())) |> should equal expected 37 | 38 | [] 39 | let ``Map3 returns Error when value is Error<(TFst, TSnd, TTrd)>`` () = 40 | let error = Errors.Error(errorMessage) 41 | let value = Result.Error(error) 42 | let expected = Result.Error(error) 43 | TupleResult.Map3(value, (fun _ _ _ -> "")) |> should equal expected 44 | 45 | module Bind2 = 46 | 47 | let testTuple = struct ("Meaning of Life", 42) 48 | let errorMessage = "Some error message." 49 | 50 | [] 51 | let ``Bind2 returns result of binder when value is Ok<(TFst, TSnd)>`` () = 52 | let value = Result.Ok(testTuple) 53 | let expected = Result.Ok(testTuple.ToString()) 54 | TupleResult.Bind2(value, (fun x y -> Result.Ok((x, y).ToString()))) |> should equal expected 55 | 56 | [] 57 | let ``Bind2 returns Error when value is Error<(TFst, TSnd)>`` () = 58 | let error = Errors.Error(errorMessage) 59 | let value = Result.Error(error) 60 | let expected = Result.Error(error) 61 | TupleResult.Bind2(value, (fun _ _ -> Result.Ok(""))) |> should equal expected 62 | 63 | module Bind3 = 64 | 65 | let testTuple = struct ("Meaning of Life", 42, 0.1) 66 | let errorMessage = "Some error message." 67 | 68 | [] 69 | let ``Bind3 returns result of binder when value is Ok<(TFst, TSnd, TTrd)>`` () = 70 | let value = Result.Ok(testTuple) 71 | let expected = Result.Ok(testTuple.ToString()) 72 | TupleResult.Bind3(value, (fun x y z -> Result.Ok((x, y, z).ToString()))) |> should equal expected 73 | 74 | [] 75 | let ``Bind3 returns Error when value is Error<(TFst, TSnd, TTrd)>`` () = 76 | let error = Errors.Error(errorMessage) 77 | let value = Result.Error(error) 78 | let expected = Result.Error(error) 79 | TupleResult.Bind3(value, (fun _ _ _ -> Result.Ok(""))) |> should equal expected 80 | 81 | module Match2 = 82 | 83 | let testTuple = struct ("Meaning of Life", 42) 84 | let errorMessage = "Some error message." 85 | 86 | [] 87 | let ``Match2 returns result of ok when value is Ok<(TFst, TSnd)>`` () = 88 | let result = Result.Ok(testTuple) 89 | let expected = testTuple.ToString() 90 | TupleResult.Match2(result, 91 | ok = (fun a b -> (a,b).ToString()), 92 | error = (fun e -> e.Message.Value)) 93 | |> should equal expected 94 | 95 | [] 96 | let ``Match2 returns result of error when value is Error<(TFst, TSnd)>`` () = 97 | let error = Errors.Error(errorMessage) 98 | let result = Result.Error(error) 99 | TupleResult.Match2(result, 100 | ok = (fun _ _ -> ""), 101 | error = (fun e -> e.Message.Value)) 102 | |> should equal errorMessage 103 | 104 | [] 105 | let ``MatchTo2 returns result of ok when value is Ok<(TFst, TSnd)>`` () = 106 | let result = Result.Ok(testTuple) 107 | TupleResult.MatchTo2(result, "Success", "Failure") |> should equal "Success" 108 | 109 | [] 110 | let ``MatchTo2 returns result of error when value is Error<(TFst, TSnd)>`` () = 111 | let error = Errors.Error(errorMessage) 112 | let result = Result.Error(error) 113 | TupleResult.MatchTo2(result, "Success", "Failure") |> should equal "Failure" 114 | 115 | module Match3 = 116 | 117 | let testTuple = struct ("Some stringo", 101, 2.0) 118 | let errorMessage = "Some error message." 119 | 120 | [] 121 | let ``Match3 returns result of ok when value is Ok<(TFst, TSnd, TTrd)>`` () = 122 | let result = Result.Ok(testTuple) 123 | let expected = testTuple.ToString() 124 | TupleResult.Match3(result, 125 | ok = (fun a b c -> (a, b, c).ToString()), 126 | error = (fun e -> e.Message.Value)) 127 | |> should equal expected 128 | 129 | [] 130 | let ``Match3 returns result of error when value is Error`` () = 131 | let error = Errors.Error(errorMessage) 132 | let value = Result.Error(error) 133 | TupleResult.Match3(value, 134 | ok = (fun _ _ _ -> ""), 135 | error = (fun e -> e.Message.Value)) 136 | |> should equal errorMessage 137 | 138 | [] 139 | let ``MatchTo3 returns result of ok when value is Ok<(TFst, TSnd, TTrd)>`` () = 140 | let result = Result.Ok(testTuple) 141 | TupleResult.MatchTo3(result, "Success", "Failure") |> should equal "Success" 142 | 143 | [] 144 | let ``MatchTo2 returns result of error when value is Error<(TFst, TSnd, TTrd)>`` () = 145 | let error = Errors.Error(errorMessage) 146 | let result = Result.Error(error) 147 | TupleResult.MatchTo3(result, "Success", "Failure") |> should equal "Failure" 148 | 149 | module ``Side effects (2 value tuple)`` = 150 | 151 | let testTuple = struct ("Meaning of Life", 42) 152 | let errorMessage = "Some error message." 153 | 154 | [] 155 | let ``Do2 returns value and executes action when value is Ok<(TFst, TSnd)>`` () = 156 | let value = Result.Ok(testTuple) 157 | let expected = testTuple.ToString() 158 | let mutable result = "" 159 | TupleResult.Do2(value, (fun a b -> result <- (struct (a, b)).ToString())) |> should equal value 160 | result |> should equal expected 161 | 162 | [] 163 | let ``Do2 returns value and doesn't execute action when value is Error<(TFst, TSnd)>`` () = 164 | let value = Result.Error(Errors.Error()) 165 | let expected = "expected" 166 | let mutable result = expected 167 | TupleResult.Do2(value, (fun _ _ -> result <- errorMessage)) |> should equal value 168 | result |> should equal expected 169 | 170 | module ``Side effects (3 value tuple)`` = 171 | 172 | let testTuple = struct ("Some stringo", 101, 2.0) 173 | let errorMessage = "Some error message." 174 | 175 | [] 176 | let ``Do3 returns value and executes action when value is Ok<(TFst, TSnd, TTrd)>`` () = 177 | let value = Result.Ok(testTuple) 178 | let expected = testTuple.ToString() 179 | let mutable result = "" 180 | TupleResult.Do3(value, (fun a b c -> result <- (struct (a, b, c)).ToString())) |> should equal value 181 | result |> should equal expected 182 | 183 | [] 184 | let ``Do3 returns value and doesn't execute action when value is Error<(TFst, TSnd, TTrd)>`` () = 185 | let value = Result.Error(Errors.Error()) 186 | let expected = "expected" 187 | let mutable result = expected 188 | TupleResult.Do3(value, (fun _ _ _ -> result <- errorMessage)) |> should equal value 189 | result |> should equal expected 190 | 191 | module TryCatch2 = 192 | 193 | let testTuple = struct ("Meaning of Life", 42) 194 | let errorMessage = "Some error message." 195 | 196 | [] 197 | let ``TryCatch2 returns Ok when previous result is Ok<(TFst, TSnd)> and function call doesn't throw`` () = 198 | let result = Result.Ok(testTuple) 199 | TupleResult.TryCatch2(result, (fun a b -> struct (a, b)), (fun _ _ _ -> Errors.Error())) |> should equal (Result.Ok(testTuple)) 200 | 201 | [] 202 | let ``TryCatch2 returns Error when previous result is Ok<(TFst, TSnd)> and function call throws`` () = 203 | let message = errorMessage 204 | let result = Result.Ok(testTuple) 205 | TupleResult.TryCatch2(result, 206 | tryFunc = (fun _ _ -> raise(Exception(message))), 207 | errorHandler = (fun a b e -> Errors.Error((struct (a, b)).ToString() + e.Message))) 208 | |> should equal (Result.Error(Errors.Error(testTuple.ToString() + message))) 209 | 210 | [] 211 | let ``TryCatch2 returns Error when previous result is Error<(TFst, TSnd)>`` () = 212 | let error = Errors.Error(errorMessage) 213 | let result = Result.Error(error) 214 | TupleResult.TryCatch2(result, 215 | tryFunc = (fun _ _ -> "This should be omitted."), 216 | errorHandler = (fun _ _ _ -> Errors.Error())) |> should equal (Result.Error(error)) 217 | 218 | module TryCatch3 = 219 | 220 | let testTuple = struct ("Some stringo", 101, 2.0) 221 | let errorMessage = "Some error message." 222 | 223 | [] 224 | let ``TryCatch3 returns Ok when previous result is Ok<(TFst, TSnd)> and function call doesn't throw`` () = 225 | let result = Result.Ok(testTuple) 226 | TupleResult.TryCatch3(result, (fun a b c -> struct (a, b, c)), (fun _ _ _ _ -> Errors.Error())) |> should equal (Result.Ok(testTuple)) 227 | 228 | [] 229 | let ``TryCatch3 returns Error when previous result is Ok<(TFst, TSnd)> and function call throws`` () = 230 | let message = errorMessage 231 | let result = Result.Ok(testTuple) 232 | TupleResult.TryCatch3(result, 233 | tryFunc = (fun _ _ _ -> raise(Exception(message))), 234 | errorHandler = (fun a b c e -> Errors.Error((struct (a, b, c)).ToString() + e.Message))) 235 | |> should equal (Result.Error(Errors.Error(testTuple.ToString() + message))) 236 | 237 | [] 238 | let ``TryCatch3 returns Error when previous result is Error`` () = 239 | let error = Errors.Error(errorMessage) 240 | let result = Result.Error(error) 241 | TupleResult.TryCatch2(result, 242 | tryFunc = (fun _ _ -> "This should be omitted."), 243 | errorHandler = (fun _ _ _ -> Errors.Error())) |> should equal (Result.Error(error)) 244 | -------------------------------------------------------------------------------- /Monacs.Core/Result.Extensions.Tuple.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static Monacs.Core.Result; 3 | 4 | namespace Monacs.Core 5 | { 6 | /// 7 | /// Contains the set of tuple extensions to work with the type. 8 | /// 9 | public static class TupleResult 10 | { 11 | /* Map */ 12 | 13 | /// 14 | /// Maps the value of the into another using the function. 15 | /// If the input result is Ok, returns the Ok case with the value of the mapper call (which is ). 16 | /// Otherwise returns Error case of the Result of . 17 | /// 18 | /// Type of first value in input result. 19 | /// Type of second value in input result. 20 | /// Type of the value in the returned result. 21 | /// The result to map on. 22 | /// Function called with the input result value if it's Ok case. 23 | public static Result Map2(this Result<(TFst fst, TSnd snd)> result, Func mapper) => 24 | result.IsOk ? Ok(mapper(result.Value.fst, result.Value.snd)) : Error(result.Error); 25 | 26 | /// 27 | /// Maps the value of the into another using the function. 28 | /// If the input result is Ok, returns the Ok case with the value of the mapper call (which is ). 29 | /// Otherwise returns Error case of the Result of . 30 | /// 31 | /// Type of first value in input result. 32 | /// Type of second value in input result. 33 | /// Type of third value in input result. 34 | /// Type of the value in the returned result. 35 | /// The result to map on. 36 | /// Function called with the input result value if it's Ok case. 37 | public static Result Map3(this Result<(TFst fst, TSnd snd, TTrd trd)> result, Func mapper) => 38 | result.IsOk ? Ok(mapper(result.Value.fst, result.Value.snd, result.Value.trd)) : Error(result.Error); 39 | 40 | /* Bind */ 41 | 42 | /// 43 | /// Applies railway pattern and binds two functions. 44 | /// If the result of the previous function is on the success path, the received result is taken as an argument and the next function is invoked. 45 | /// If the result of the previous function is on the failure path, the new error is created to match generic result type, but the error details remain the same. 46 | /// 47 | /// Type of the first output tuple value received from previous function. 48 | /// Type of the second output tuple value received from previous function. 49 | /// Type of the output value. 50 | /// Output of previous function 51 | /// Passes the output of first function to the next one. 52 | /// Result of the second function or error received from the first function. 53 | public static Result Bind2(this Result<(TFst fst, TSnd snd)> result, Func> binder) => 54 | result.IsOk ? binder(result.Value.fst, result.Value.snd) : Error(result.Error); 55 | 56 | /// 57 | /// Applies railway pattern and binds two functions. 58 | /// If the result of the previous function is on the success path, the received result is taken as an argument and the next function is invoked. 59 | /// If the result of the previous function is on the failure path, the new error is created to match generic result type, but the error details remain the same. 60 | /// 61 | /// Type of the first output tuple value received from previous function. 62 | /// Type of the second output tuple value received from previous function. 63 | /// Type of the second output tuple value received from previous function. 64 | /// Type of the output value. 65 | /// Output of previous function 66 | /// Passes the output of first function to the next one. 67 | /// Result of the second function or error received from the first function. 68 | public static Result Bind3(this Result<(TFst fst, TSnd snd, TTrd trd)> result, Func> binder) => 69 | result.IsOk ? binder(result.Value.fst, result.Value.snd, result.Value.trd) : Error(result.Error); 70 | 71 | /* Match */ 72 | 73 | /// 74 | /// Does the pattern matching on the type. 75 | /// If the is Ok, calls function 76 | /// with the value from the result as a parameter and returns its result. 77 | /// Otherwise calls function and returns its result. 78 | /// 79 | /// Type of the first tuple value in the result. 80 | /// Type of the second tuple value in the result. 81 | /// Type of the returned value. 82 | /// The result to match on. 83 | /// Function called for the Ok case. 84 | /// Function called for the Error case. 85 | public static TResult Match2(this Result<(TFst fst, TSnd snd)> result, Func ok, Func error) => 86 | result.IsOk ? ok(result.Value.fst, result.Value.snd) : error(result.Error); 87 | 88 | /// 89 | /// Does the pattern matching on the type. 90 | /// If the is Ok, calls function 91 | /// with the value from the result as a parameter and returns its result. 92 | /// Otherwise calls function and returns its result. 93 | /// 94 | /// Type of the first tuple value in the result. 95 | /// Type of the second tuple value in the result. 96 | /// Type of the third tuple value in the result. 97 | /// Type of the returned value. 98 | /// The result to match on. 99 | /// Function called for the Ok case. 100 | /// Function called for the Error case. 101 | public static TResult Match3(this Result<(TFst fst, TSnd snd, TTrd trd)> result, Func ok, Func error) => 102 | result.IsOk ? ok(result.Value.fst, result.Value.snd, result.Value.trd) : error(result.Error); 103 | 104 | /// 105 | /// Does the pattern matching on the type. 106 | /// If the is Ok, returns value. 107 | /// Otherwise returns value. 108 | /// 109 | /// Type of the first tuple value in the result. 110 | /// Type of the second tuple value in the result. 111 | /// Type of the returned value. 112 | /// The result to match on. 113 | /// Value returned for the Ok case. 114 | /// Value returned for the Error case. 115 | public static TResult MatchTo2(this Result<(TFst fst, TSnd snd)> result, TResult ok, TResult error) => 116 | result.IsOk ? ok : error; 117 | 118 | /// 119 | /// Does the pattern matching on the type. 120 | /// If the is Ok, returns value. 121 | /// Otherwise returns value. 122 | /// 123 | /// Type of the first tuple value in the result. 124 | /// Type of the second tuple value in the result. 125 | /// Type of the third tuple value in the result. 126 | /// Type of the returned value. 127 | /// The result to match on. 128 | /// Value returned for the Ok case. 129 | /// Value returned for the Error case. 130 | public static TResult MatchTo3(this Result<(TFst fst, TSnd snd, TTrd trd)> result, TResult ok, TResult error) => 131 | result.IsOk ? ok : error; 132 | 133 | /* Do */ 134 | 135 | /// 136 | /// Performs action if the given result is on the succesful path. 137 | /// The action takes given result as an argument. 138 | /// 139 | /// Type of first tuple result value. 140 | /// Type of second tuple result value. 141 | /// Given result. 142 | /// Action to perform. 143 | /// Result passed to the method as an argument. 144 | public static Result<(TFst, TSnd)> Do2(this Result<(TFst fst, TSnd snd)> result, Action action) 145 | { 146 | if (result.IsOk) 147 | action(result.Value.fst, result.Value.snd); 148 | return result; 149 | } 150 | 151 | /// 152 | /// Performs action if the given result is on the succesful path. 153 | /// The action takes given result as an argument. 154 | /// 155 | /// Type of first tuple result value. 156 | /// Type of second tuple result value. 157 | /// Type of third tuple result value. 158 | /// Given result. 159 | /// Action to perform. 160 | /// Result passed to the method as an argument. 161 | public static Result<(TFst, TSnd, TTrd)> Do3(this Result<(TFst fst, TSnd snd, TTrd trd)> result, Action action) 162 | { 163 | if (result.IsOk) 164 | action(result.Value.fst, result.Value.snd, result.Value.trd); 165 | return result; 166 | } 167 | 168 | /* TryCatch */ 169 | 170 | /// 171 | /// Invokes function in try/catch block and returns its result. 172 | /// If any is raised during execution, error handler is invoked and error details are returned. 173 | /// 174 | /// Type of first tuple result value. 175 | /// Type of second tuple result value. 176 | /// Type of value returned by invoked function. 177 | /// Result to take the value from. 178 | /// The function to be invoked in 'try' block. 179 | /// Handler invoked in 'catch' block on any raised exception. 180 | /// of invoked function in try block or if any exception occurs. 181 | public static Result TryCatch2(this Result<(TFst fst, TSnd snd)> result, Func tryFunc, Func errorHandler) => 182 | result.Bind(value => TryCatch(func: () => tryFunc(value.fst, value.snd), errorHandler: err => errorHandler(value.fst, value.snd, err))); 183 | 184 | /// 185 | /// Invokes function in try/catch block and returns its result. 186 | /// If any is raised during execution, error handler is invoked and error details are returned. 187 | /// 188 | /// Type of first tuple result value. 189 | /// Type of second tuple result value. 190 | /// Type of third tuple result value. 191 | /// Type of value returned by invoked function. 192 | /// Result to take the value from. 193 | /// The function to be invoked in 'try' block. 194 | /// Handler invoked in 'catch' block on any raised exception. 195 | /// of invoked function in try block or if any exception occurs. 196 | public static Result TryCatch3(this Result<(TFst fst, TSnd snd, TTrd trd)> result, Func tryFunc, Func errorHandler) => 197 | result.Bind(value => TryCatch(func: () => tryFunc(value.fst, value.snd, value.trd), errorHandler: err => errorHandler(value.fst, value.snd, value.trd, err))); 198 | 199 | } 200 | } -------------------------------------------------------------------------------- /Monacs.UnitTests/OptionTests.fs: -------------------------------------------------------------------------------- 1 | namespace Monacs.UnitTests.OptionTests 2 | 3 | open System 4 | open Xunit 5 | open FsUnit.Xunit 6 | 7 | open Monacs.Core 8 | 9 | module ``Constructors and equality`` = 10 | 11 | [] 12 | let ``Some equals itself`` () = 13 | let opt = Option.Some("test") 14 | opt = opt |> should equal true 15 | opt <> opt |> should equal false 16 | 17 | [] 18 | let ``Some doesn't equal null`` () = 19 | let opt = box (Option.Some("test")) 20 | opt = null |> should equal false 21 | opt <> null |> should equal true 22 | 23 | [] 24 | let ``None equals itself`` () = 25 | let opt = Option.None() 26 | opt = opt |> should equal true 27 | opt <> opt |> should equal false 28 | 29 | [] 30 | let ``None doesn't equal null`` () = 31 | let opt = box (Option.None()) 32 | opt = null |> should equal false 33 | opt <> null |> should equal true 34 | 35 | [] 36 | let ``None equals None`` () = 37 | Option.None() = Option.None() |> should equal true 38 | Option.None() <> Option.None() |> should equal false 39 | 40 | [] 41 | let ``Some equals Some when the Value is equal`` () = 42 | let value = "test" 43 | Option.Some(value) = Option.Some(value) |> should equal true 44 | Option.Some(value) <> Option.Some(value) |> should equal false 45 | 46 | [] 47 | let ``Some doesn't equal Some when the Value is not equal`` () = 48 | Option.Some(42) = Option.Some(13) |> should equal false 49 | Option.Some(42) <> Option.Some(13) |> should equal true 50 | 51 | [] 52 | let ``Some doesn't equal None`` () = 53 | Option.Some(42) = Option.None() |> should equal false 54 | Option.Some(42) <> Option.None() |> should equal true 55 | 56 | module Converters = 57 | 58 | [] 59 | let ``OfObject returns None when value is null`` () = 60 | Option.OfObject(null) |> should equal (Option.None()) 61 | 62 | [] 63 | let ``OfObject returns Some when value is not null`` () = 64 | let object = obj() 65 | Option.OfObject(object) |> should equal (Option.Some(object)) 66 | 67 | [] 68 | let ``OfNullable returns None when value is null`` () = 69 | let empty = new Nullable() 70 | Option.OfNullable(empty) |> should equal (Option.None()) 71 | 72 | [] 73 | let ``OfNullable returns Some when value is not null`` () = 74 | let value = Nullable(42) 75 | Option.OfNullable(value) |> should equal (Option.Some(value.Value)) 76 | 77 | [] 78 | let ``ToNullable returns null when value is None`` () = 79 | let none = Option.None() 80 | Option.ToNullable(none) |> should equal (new Nullable()) 81 | 82 | [] 83 | let ``ToNullable returns value when value is Some`` () = 84 | let some = Option.Some(42) 85 | Option.ToNullable(some) |> should equal (Nullable(some.Value)) 86 | 87 | [] 88 | let ``OfResult returns None when value Error`` () = 89 | let error = Result.Error(Errors.Info()) 90 | Option.OfResult(error) |> should equal (Option.None()) 91 | 92 | [] 93 | let ``OfResult returns Some when value is Ok`` () = 94 | let result = Result.Ok(42) 95 | Option.OfResult(result) |> should equal (Option.Some(result.Value)) 96 | 97 | [] 98 | let ``OfString returns None when value is null`` () = 99 | Option.OfString(null) |> should equal (Option.None()) 100 | 101 | [] 102 | let ``OfString returns None when value is empty`` () = 103 | Option.OfString("") |> should equal (Option.None()) 104 | 105 | [] 106 | let ``OfString returns Some when value is not null and not empty`` () = 107 | let value = "test" 108 | Option.OfString(value) |> should equal (Option.Some(value)) 109 | 110 | module TryGetOption = 111 | open System.Collections.Generic 112 | open System.Linq 113 | 114 | [] 115 | let ``TryGetOption returns None when key is not present in dictionary`` () = 116 | let dict = new Dictionary() 117 | Option.TryGetOption(dict, 1) |> should equal (Option.None()) 118 | 119 | [] 120 | let ``TryGetOption returns Some when key is present in dictionary`` () = 121 | let dict = new Dictionary() 122 | let key = 42 123 | let value = "hello" 124 | dict.Add(42, value) 125 | Option.TryGetOption(dict, key) |> should equal (Option.Some(value)) 126 | 127 | [] 128 | let ``TryGetOption returns None> when key is not present in lookup`` () = 129 | let lookup = [1].ToLookup(fun k -> k) 130 | Option.TryGetOption(lookup, 2) |> should equal (Option.None>()) 131 | 132 | [] 133 | let ``TryGetOption returns Some> when key is present in lookup`` () = 134 | let key = 42 135 | let value = "hello" 136 | let lookup = [(key, value)].ToLookup((fun (k, _) -> k), (fun (_, v) -> v)) 137 | let result = Option.TryGetOption(lookup, key) 138 | result.IsSome |> should equal true 139 | result.Value.Count() |> should equal 1 140 | result.Value |> should contain value 141 | 142 | module Match = 143 | 144 | [] 145 | let ``Match returns result of none when value is None`` () = 146 | let value = Option.None() 147 | let expected = "test" 148 | Option.Match(value, some = (fun _ -> ""), none = (fun () -> expected)) |> should equal expected 149 | 150 | [] 151 | let ``Match returns result of some when value is Some`` () = 152 | let value = Option.Some(42) 153 | let expected = "test" 154 | Option.Match(value, some = (fun _ -> expected), none = (fun () -> "")) |> should equal expected 155 | 156 | [] 157 | let ``MatchTo returns result of none when value is None`` () = 158 | let value = Option.None() 159 | let expected = "test" 160 | Option.MatchTo(value, some = "", none = expected) |> should equal expected 161 | 162 | [] 163 | let ``MatchTo returns result of some when value is Some`` () = 164 | let value = Option.Some(42) 165 | let expected = "test" 166 | Option.MatchTo(value, some = expected, none = "") |> should equal expected 167 | 168 | module Bind = 169 | 170 | [] 171 | let ``Bind returns result of binder when value is Some`` () = 172 | let value = Option.Some(42) 173 | let expected = Option.Some("42") 174 | Option.Bind(value, (fun x -> Option.Some(x.ToString()))) |> should equal expected 175 | 176 | [] 177 | let ``Bind returns None when value is None`` () = 178 | let value = Option.None() 179 | let expected = Option.None() 180 | Option.Bind(value, (fun x -> Option.Some(x.ToString()))) |> should equal expected 181 | 182 | module Map = 183 | 184 | [] 185 | let ``Map returns result of mapper when value is Some`` () = 186 | let value = Option.Some(42) 187 | let expected = Option.Some("42") 188 | Option.Map(value, (fun x -> x.ToString())) |> should equal expected 189 | 190 | [] 191 | let ``Map returns None when value is None`` () = 192 | let value = Option.None() 193 | let expected = Option.None() 194 | Option.Map(value, (fun x -> x.ToString())) |> should equal expected 195 | 196 | module GetOrDefault = 197 | 198 | [] 199 | let ``GetOrDefault returns encapsulated value when value is Some`` () = 200 | let value = Option.Some("test") 201 | let expected = "test" 202 | Option.GetOrDefault(value) |> should equal expected 203 | 204 | [] 205 | let ``GetOrDefault returns type default when value is None`` () = 206 | let value = Option.None() 207 | let expected = null 208 | Option.GetOrDefault(value) |> should equal expected 209 | 210 | [] 211 | let ``GetOrDefault returns given default when value is None`` () = 212 | let value = Option.None() 213 | let expected = "test" 214 | Option.GetOrDefault(value, whenNone = expected) |> should equal expected 215 | 216 | [] 217 | let ``GetOrDefault returns getter result when value is Some`` () = 218 | let value = Option.Some((1, "test")) 219 | let expected = "test" 220 | Option.GetOrDefault(value, fun (_, x) -> x) |> should equal expected 221 | 222 | [] 223 | let ``GetOrDefault returns type default when value is None`` () = 224 | let value = Option.None() 225 | let expected = 0 226 | Option.GetOrDefault(value, getter = (fun x -> x.GetHashCode())) |> should equal expected 227 | 228 | [] 229 | let ``GetOrDefault returns given default when value is None`` () = 230 | let value = Option.None() 231 | let expected = 42 232 | Option.GetOrDefault(value, getter = (fun x -> x.GetHashCode()), whenNone = expected) |> should equal expected 233 | 234 | module ``Side effects`` = 235 | 236 | [] 237 | let ``Do returns value and executes action when value is Some`` () = 238 | let value = Option.Some(42) 239 | let expected = "42" 240 | let mutable result = "" 241 | Option.Do(value, (fun _ -> result <- expected.ToString())) |> should equal value 242 | result |> should equal expected 243 | 244 | [] 245 | let ``Do returns value and doesn't execute action when value is None`` () = 246 | let value = Option.None() 247 | let expected = "test" 248 | let mutable result = expected 249 | Option.Do(value, (fun _ -> result <- "fail")) |> should equal value 250 | result |> should equal expected 251 | 252 | [] 253 | let ``DoWhenNone returns value and doesn't execute action when value is Some`` () = 254 | let value = Option.Some(42) 255 | let expected = "test" 256 | let mutable result = expected 257 | Option.DoWhenNone(value, (fun _ -> result <- "fail")) |> should equal value 258 | result |> should equal expected 259 | 260 | [] 261 | let ``Do returns value and executes action when value is None`` () = 262 | let value = Option.None() 263 | let expected = "42" 264 | let mutable result = "" 265 | Option.DoWhenNone(value, (fun _ -> result <- expected.ToString())) |> should equal value 266 | result |> should equal expected 267 | 268 | module Collections = 269 | 270 | [] 271 | let ``Choose returns collection of values of items which are not None`` () = 272 | let values = seq { yield Option.Some(42); yield Option.None(); yield Option.Some(123) } 273 | let expected = [| 42; 123 |] 274 | Option.Choose(values) |> Seq.toArray |> should equal expected 275 | 276 | [] 277 | let ``Sequence returns collection of values wrapped into Option when all items are not None`` () = 278 | let values = seq { yield Option.Some(42); yield Option.Some(123) } 279 | let expected = [| 42; 123 |] 280 | let result = Option.Sequence(values) 281 | result.IsSome |> should equal true 282 | result.Value |> Seq.toArray |> should equal expected 283 | 284 | [] 285 | let ``Sequence returns None> when any item is None`` () = 286 | let values = seq { yield Option.Some(42); yield Option.Some(123); yield Option.None() } 287 | let expected = Option.None() 288 | Option.Sequence(values) |> should equal expected 289 | 290 | [] 291 | let ``TryFind returns None when collection is empty`` () = 292 | let values : int list = [] 293 | let expected = Option.None() 294 | Option.TryFind(values, (fun _ -> true)) |> should equal expected 295 | 296 | [] 297 | let ``TryFind returns None when no item matches predicate`` () = 298 | let values = [1; 2; 3] 299 | let expected = Option.None() 300 | Option.TryFind(values, (fun i -> i = 4)) |> should equal expected 301 | 302 | [] 303 | let ``TryFind returns Some when any item matches predicate`` () = 304 | let values = [1; 2; 3] 305 | let expectedValue = 2 306 | let expected = Option.Some(expectedValue) 307 | Option.TryFind(values, (fun i -> i = expectedValue)) |> should equal expected 308 | 309 | [] 310 | let ``TryFirst returns None when parameter is empty list`` () = 311 | let values : System.Collections.Generic.List = new System.Collections.Generic.List() 312 | let expected = Option.None() 313 | Option.TryFirst(values) |> should equal expected 314 | 315 | [] 316 | let ``TryFirst returns None when parameter is empty sequence`` () = 317 | let values : int seq = [] |> Seq.ofList 318 | let expected = Option.None() 319 | Option.TryFirst(values) |> should equal expected 320 | 321 | [] 322 | let ``TryFirst returns Some when parameter is non-empty list`` () = 323 | let values : System.Collections.Generic.List = new System.Collections.Generic.List([1; 2; 3]) 324 | let expected = Option.Some(values.[0]) 325 | Option.TryFirst(values) |> should equal expected 326 | 327 | [] 328 | let ``TryFirst returns Some when parameter is non-empty sequence`` () = 329 | let values = [1; 2; 3] 330 | let expected = Option.Some(values.[0]) 331 | Option.TryFirst(values |> Seq.ofList) |> should equal expected 332 | 333 | [] 334 | let ``TryElementAt returns None when parameter is empty list`` () = 335 | let values : System.Collections.Generic.List = new System.Collections.Generic.List() 336 | let expected = Option.None() 337 | Option.TryElementAt(values, 0) |> should equal expected 338 | 339 | [] 340 | let ``TryElementAt returns None when parameter is empty sequence`` () = 341 | let values : int seq = [] |> Seq.ofList 342 | let expected = Option.None() 343 | Option.TryElementAt(values, 2) |> should equal expected 344 | 345 | [] 346 | let ``TryElementAt returns None when parameter is non-empty list and doesn't contain nth element`` () = 347 | let values : System.Collections.Generic.List = new System.Collections.Generic.List([1; 2; 3]) 348 | let expected = Option.None() 349 | Option.TryElementAt(values, 4) |> should equal expected 350 | 351 | [] 352 | let ``TryElementAt returns None when parameter is non-empty sequence and doesn't contain nth element`` () = 353 | let values = [1; 2; 3] 354 | let expected = Option.None() 355 | Option.TryElementAt(values |> Seq.ofList, 3) |> should equal expected 356 | 357 | [] 358 | let ``TryElementAt returns Some when parameter is non-empty list and contains nth element`` () = 359 | let values : System.Collections.Generic.List = new System.Collections.Generic.List([1; 2; 3]) 360 | let expected = Option.Some(values.[2]) 361 | Option.TryElementAt(values, 2) |> should equal expected 362 | 363 | [] 364 | let ``TryElementAt returns Some when parameter is non-empty sequence and contains nth element`` () = 365 | let values = [1; 2; 3] 366 | let expected = Option.Some(values.[1]) 367 | Option.TryElementAt(values |> Seq.ofList, 1) |> should equal expected -------------------------------------------------------------------------------- /Monacs.UnitTests/ResultTests.fs: -------------------------------------------------------------------------------- 1 | namespace Monacs.UnitTests.ResultTests 2 | 3 | open System 4 | open Xunit 5 | open FsUnit.Xunit 6 | 7 | open Monacs.Core 8 | 9 | module ``Constructors and equality`` = 10 | 11 | [] 12 | let ``Ok equals itself`` () = 13 | let result = Result.Ok("test") 14 | result = result |> should equal true 15 | result <> result |> should equal false 16 | 17 | [] 18 | let ``Ok doesn't equal null`` () = 19 | let result = box (Result.Ok("test")) 20 | result = null |> should equal false 21 | result <> null |> should equal true 22 | 23 | [] 24 | let ``Error equals itself`` () = 25 | let result = Result.Error(Errors.Debug("test")) 26 | result = result |> should equal true 27 | result <> result |> should equal false 28 | 29 | [] 30 | let ``Error doesn't equal null`` () = 31 | let result = box (Result.Error(Errors.Debug("test"))) 32 | result = null |> should equal false 33 | result <> null |> should equal true 34 | 35 | [] 36 | let ``Ok equals Ok when the Value is equal`` () = 37 | let value = "test" 38 | Result.Ok(value) = Result.Ok(value) |> should equal true 39 | Result.Ok(value) <> Result.Ok(value) |> should equal false 40 | 41 | [] 42 | let ``Ok doesn't equal Ok when the Value is not equal`` () = 43 | Result.Ok(42) = Result.Ok(13) |> should equal false 44 | Result.Ok(42) <> Result.Ok(13) |> should equal true 45 | 46 | [] 47 | let ``Error equals Error when the Error is equal`` () = 48 | let error = Errors.Error() 49 | Result.Error(error) = Result.Error(error) |> should equal true 50 | Result.Error(error) <> Result.Error(error) |> should equal false 51 | 52 | [] 53 | let ``Error doesn't equal Error when the Error is not equal`` () = 54 | let error1 = Errors.Error("test1") 55 | let error2 = Errors.Error("test2") 56 | Result.Error(error1) = Result.Error(error2) |> should equal false 57 | Result.Error(error1) <> Result.Error(error2) |> should equal true 58 | 59 | [] 60 | let ``Ok doesn't equal Error`` () = 61 | let error = Errors.Error() 62 | let value = 42 63 | Result.Ok(value) = Result.Error(error) |> should equal false 64 | Result.Ok(value) <> Result.Error(error) |> should equal true 65 | 66 | module Converters = 67 | 68 | [] 69 | let ``OfObject returns Error when value is null`` () = 70 | let error = Errors.Error() 71 | Result.OfObject(null, error) |> should equal (Result.Error(error)) 72 | 73 | [] 74 | let ``OfObject returns Ok when value is not null`` () = 75 | let object = obj() 76 | Result.OfObject(object, Errors.Error()) |> should equal (Result.Ok(object)) 77 | 78 | [] 79 | let ``OfObject with func returns Error when value is null`` () = 80 | let error = Errors.Error() 81 | Result.OfObject(null, (fun () -> error)) |> should equal (Result.Error(error)) 82 | 83 | [] 84 | let ``OfObject with func returns Ok when value is not null`` () = 85 | let object = obj() 86 | Result.OfObject(object, (fun () -> Errors.Error())) |> should equal (Result.Ok(object)) 87 | 88 | [] 89 | let ``OfNullable returns Error when value is null`` () = 90 | let empty = new Nullable() 91 | let error = Errors.Error() 92 | Result.OfNullable(empty, error) |> should equal (Result.Error(error)) 93 | 94 | [] 95 | let ``OfNullable returns Ok when value is not null`` () = 96 | let value = Nullable(42) 97 | Result.OfNullable(value, Errors.Error()) |> should equal (Result.Ok(value.Value)) 98 | 99 | [] 100 | let ``OfNullable with func returns Error when value is null`` () = 101 | let empty = new Nullable() 102 | let error = Errors.Error() 103 | Result.OfNullable(empty, (fun () -> error)) |> should equal (Result.Error(error)) 104 | 105 | [] 106 | let ``OfNullable with func returns Ok when value is not null`` () = 107 | let value = Nullable(42) 108 | Result.OfNullable(value, (fun () -> Errors.Error())) |> should equal (Result.Ok(value.Value)) 109 | 110 | [] 111 | let ``OfString returns Error when value is null`` () = 112 | let error = Errors.Error() 113 | Result.OfString(null, error) |> should equal (Result.Error(error)) 114 | 115 | [] 116 | let ``OfString returns Error when value is empty`` () = 117 | let error = Errors.Error() 118 | Result.OfString("", error) |> should equal (Result.Error(error)) 119 | 120 | [] 121 | let ``OfString returns Ok when value is not null and not empty`` () = 122 | let value = "test" 123 | Result.OfString(value, Errors.Error()) |> should equal (Result.Ok(value)) 124 | 125 | [] 126 | let ``OfString with func returns Error when value is null`` () = 127 | let error = Errors.Error() 128 | Result.OfString(null, (fun () -> error)) |> should equal (Result.Error(error)) 129 | 130 | [] 131 | let ``OfString with func returns Error when value is empty`` () = 132 | let error = Errors.Error() 133 | Result.OfString("", (fun () -> error)) |> should equal (Result.Error(error)) 134 | 135 | [] 136 | let ``OfString with func returns Ok when value is not null and not empty`` () = 137 | let value = "test" 138 | Result.OfString(value, (fun () -> Errors.Error())) |> should equal (Result.Ok(value)) 139 | 140 | [] 141 | let ``OfOption returns Error when value is None`` () = 142 | let empty = Option.None() 143 | let error = Errors.Error() 144 | Result.OfOption(empty, error) |> should equal (Result.Error(error)) 145 | 146 | [] 147 | let ``OfOption returns Ok when value is Some`` () = 148 | let value = Option.Some(42) 149 | Result.OfOption(value, Errors.Error()) |> should equal (Result.Ok(value.Value)) 150 | 151 | [] 152 | let ``OfOption with func returns Error when value is None`` () = 153 | let empty = Option.None() 154 | let error = Errors.Error() 155 | Result.OfOption(empty, (fun () -> error)) |> should equal (Result.Error(error)) 156 | 157 | [] 158 | let ``OfOption with func returns Ok when value is Some`` () = 159 | let value = Option.Some(42) 160 | Result.OfOption(value, (fun () -> Errors.Error())) |> should equal (Result.Ok(value.Value)) 161 | 162 | module TryGetResult = 163 | open System.Collections.Generic 164 | open System.Linq 165 | 166 | [] 167 | let ``TryGetResult returns Error when key is not present in dictionary`` () = 168 | let dict = new Dictionary() 169 | let error = Errors.Error() 170 | Result.TryGetResult(dict, 1, error) |> should equal (Result.Error(error)) 171 | 172 | [] 173 | let ``TryGetResult returns Ok when key is present in dictionary`` () = 174 | let dict = new Dictionary() 175 | let key = 42 176 | let value = "hello" 177 | dict.Add(42, value) 178 | Result.TryGetResult(dict, key, Errors.Error()) |> should equal (Result.Ok(value)) 179 | 180 | [] 181 | let ``TryGetResult with func returns Error when key is not present in dictionary`` () = 182 | let dict = new Dictionary() 183 | let key = "foo" 184 | Result.TryGetResult(dict, key, (fun k -> Errors.Error(k))) |> should equal (Result.Error(Errors.Error(key))) 185 | 186 | [] 187 | let ``TryGetResult with func returns Ok when key is present in dictionary`` () = 188 | let dict = new Dictionary() 189 | let key = 42 190 | let value = "hello" 191 | dict.Add(42, value) 192 | Result.TryGetResult(dict, key, (fun _ -> Errors.Error())) |> should equal (Result.Ok(value)) 193 | 194 | [] 195 | let ``TryGetResult returns Error> when key is not present in lookup`` () = 196 | let lookup = [1].ToLookup(fun k -> k) 197 | let error = Errors.Error() 198 | Result.TryGetResult(lookup, 2, error) |> should equal (Result.Error>(error)) 199 | 200 | [] 201 | let ``TryGetResult returns Ok> when key is present in lookup`` () = 202 | let key = 42 203 | let value = "hello" 204 | let lookup = [(key, value)].ToLookup((fun (k, _) -> k), (fun (_, v) -> v)) 205 | let result = Result.TryGetResult(lookup, key, Errors.Error()) 206 | result.IsOk |> should equal true 207 | result.Value.Count() |> should equal 1 208 | result.Value |> should contain value 209 | 210 | [] 211 | let ``TryGetResult with func returns Error> when key is not present in lookup`` () = 212 | let lookup = ["bar"].ToLookup(fun k -> k) 213 | let key = "foo" 214 | Result.TryGetResult(lookup, key, (fun k -> Errors.Error(k))) |> should equal (Result.Error>(Errors.Error(key))) 215 | 216 | [] 217 | let ``TryGetResult with func returns Ok> when key is present in lookup`` () = 218 | let key = 42 219 | let value = "hello" 220 | let lookup = [(key, value)].ToLookup((fun (k, _) -> k), (fun (_, v) -> v)) 221 | let result = Result.TryGetResult(lookup, key, (fun _ -> Errors.Error())) 222 | result.IsOk |> should equal true 223 | result.Value.Count() |> should equal 1 224 | result.Value |> should contain value 225 | 226 | module Match = 227 | 228 | [] 229 | let ``Match returns result of error when value is Error`` () = 230 | let expected = "test" 231 | let error = Errors.Error(expected) 232 | let value = Result.Error(error) 233 | Result.Match(value, ok = (fun _ -> ""), error = (fun e -> e.Message.Value)) |> should equal expected 234 | 235 | [] 236 | let ``Match returns result of ok when value is Ok`` () = 237 | let value = Result.Ok(42) 238 | let expected = "test" 239 | Result.Match(value, ok = (fun _ -> expected), error = (fun _ -> "")) |> should equal expected 240 | 241 | [] 242 | let ``MatchTo returns result of error when value is Error`` () = 243 | let expected = "test" 244 | let value = Result.Error(Errors.Error()) 245 | Result.MatchTo(value, ok = "", error = expected) |> should equal expected 246 | 247 | [] 248 | let ``MatchTo returns result of ok when value is Ok`` () = 249 | let value = Result.Ok(42) 250 | let expected = "test" 251 | Result.MatchTo(value, ok = expected, error = "") |> should equal expected 252 | 253 | module Bind = 254 | 255 | [] 256 | let ``Bind returns result of binder when value is Ok`` () = 257 | let value = Result.Ok(42) 258 | let expected = Result.Ok("42") 259 | Result.Bind(value, (fun x -> Result.Ok(x.ToString()))) |> should equal expected 260 | 261 | [] 262 | let ``Bind returns Error when value is Error`` () = 263 | let error = Errors.Error() 264 | let value = Result.Error(error) 265 | let expected = Result.Error(error) 266 | Result.Bind(value, (fun x -> Result.Ok(x.ToString()))) |> should equal expected 267 | 268 | module Map = 269 | 270 | [] 271 | let ``Map returns result of mapper when value is Ok`` () = 272 | let value = Result.Ok(42) 273 | let expected = Result.Ok("42") 274 | Result.Map(value, (fun x -> x.ToString())) |> should equal expected 275 | 276 | [] 277 | let ``Map returns Error when value is Error`` () = 278 | let error = Errors.Error() 279 | let value = Result.Error(error) 280 | let expected = Result.Error(error) 281 | Result.Map(value, (fun x -> x.ToString())) |> should equal expected 282 | 283 | module GetOrDefault = 284 | 285 | [] 286 | let ``GetOrDefault returns encapsulated value when value is Ok`` () = 287 | let value = Result.Ok("test") 288 | let expected = "test" 289 | Result.GetOrDefault(value) |> should equal expected 290 | 291 | [] 292 | let ``GetOrDefault returns type default when value is Error`` () = 293 | let value = Result.Error(Errors.Error()) 294 | let expected = null 295 | Result.GetOrDefault(value) |> should equal expected 296 | 297 | [] 298 | let ``GetOrDefault returns given default when value is Error`` () = 299 | let value = Result.Error(Errors.Error()) 300 | let expected = "test" 301 | Result.GetOrDefault(value, whenError = expected) |> should equal expected 302 | 303 | [] 304 | let ``GetOrDefault returns getter result when value is Ok`` () = 305 | let value = Result.Ok((1, "test")) 306 | let expected = "test" 307 | Result.GetOrDefault(value, fun (_, x) -> x) |> should equal expected 308 | 309 | [] 310 | let ``GetOrDefault returns type default when value is Error`` () = 311 | let value = Result.Error(Errors.Error()) 312 | let expected = 0 313 | Result.GetOrDefault(value, getter = (fun x -> x.GetHashCode())) |> should equal expected 314 | 315 | [] 316 | let ``GetOrDefault returns given default when value is Error`` () = 317 | let value = Result.Error(Errors.Error()) 318 | let expected = 42 319 | Result.GetOrDefault(value, getter = (fun x -> x.GetHashCode()), whenError = expected) |> should equal expected 320 | 321 | module ``Side effects`` = 322 | 323 | [] 324 | let ``Do returns value and executes action when value is Ok`` () = 325 | let value = Result.Ok(42) 326 | let expected = "42" 327 | let mutable result = "" 328 | Result.Do(value, (fun _ -> result <- expected.ToString())) |> should equal value 329 | result |> should equal expected 330 | 331 | [] 332 | let ``Do returns value and doesn't execute action when value is Error`` () = 333 | let value = Result.Error(Errors.Error()) 334 | let expected = "test" 335 | let mutable result = expected 336 | Result.Do(value, (fun _ -> result <- "fail")) |> should equal value 337 | result |> should equal expected 338 | 339 | [] 340 | let ``DoWhenError returns value and doesn't execute action when value is Ok`` () = 341 | let value = Result.Ok(42) 342 | let expected = "test" 343 | let mutable result = expected 344 | Result.DoWhenError(value, (fun _ -> result <- "fail")) |> should equal value 345 | result |> should equal expected 346 | 347 | [] 348 | let ``DoWhenError returns value and executes action when value is Error`` () = 349 | let value = Result.Error(Errors.Error()) 350 | let expected = "42" 351 | let mutable result = "" 352 | Result.DoWhenError(value, (fun _ -> result <- expected.ToString())) |> should equal value 353 | result |> should equal expected 354 | 355 | module Collections = 356 | 357 | [] 358 | let ``Choose returns collection of values of items which are not Error`` () = 359 | let values = seq { yield Result.Ok(42); yield Result.Error(Errors.Error()); yield Result.Ok(123) } 360 | let expected = [| 42; 123 |] 361 | Result.Choose(values) |> Seq.toArray |> should equal expected 362 | 363 | [] 364 | let ``ChooseErrors returns collection of errors from items which are Error`` () = 365 | let expected = [| Errors.Error("1"); Errors.Info("2") |] 366 | let values = seq { yield Result.Ok(42); yield Result.Error(expected.[0]); yield Result.Ok(123); yield Result.Error(expected.[1]); } 367 | Result.ChooseErrors(values) |> Seq.toArray |> should equal expected 368 | 369 | [] 370 | let ``Sequence returns collection of values wrapped into Result when all items are not Error`` () = 371 | let values = seq { yield Result.Ok(42); yield Result.Ok(123) } 372 | let expected = [| 42; 123 |] 373 | let result = Result.Sequence(values) 374 | result.IsOk |> should equal true 375 | result.Value |> Seq.toArray |> should equal expected 376 | 377 | [] 378 | let ``Sequence returns Error> with first error when any item is Error`` () = 379 | let error = Errors.Error("error!") 380 | let values = seq { yield Result.Ok(42); yield Result.Error(error); yield Result.Error(Errors.Error()) } 381 | let expected = Result.Error(error) 382 | Result.Sequence(values) |> should equal expected 383 | 384 | module TryCatch = 385 | 386 | [] 387 | let ``TryCatch returns Ok when function call doesn't throw`` () = 388 | let value = 42 389 | Result.TryCatch((fun () -> value), (fun _ -> Errors.Error())) |> should equal (Result.Ok(value)) 390 | 391 | [] 392 | let ``TryCatch returns Error when function call throws`` () = 393 | let message = "This should not happen!" 394 | Result.TryCatch((fun () -> raise(Exception(message))), (fun e -> Errors.Error(e.Message))) |> should equal (Result.Error(Errors.Error(message))) 395 | 396 | [] 397 | let ``TryCatch returns Ok when previous result is Ok and function call doesn't throw`` () = 398 | let result = Result.Ok(42) 399 | let value = "42" 400 | Result.TryCatch(result, (fun v -> v.ToString()), (fun _ _ -> Errors.Error())) |> should equal (Result.Ok(value)) 401 | 402 | [] 403 | let ``TryCatch returns Error when previous result is Ok and function call throws`` () = 404 | let value = "OK" 405 | let message = "This should not happen!" 406 | let result = Result.Ok(value) 407 | Result.TryCatch(result, (fun _ -> raise(Exception(message))), (fun v e -> Errors.Error(v + e.Message))) |> should equal (Result.Error(Errors.Error(value + message))) 408 | 409 | [] 410 | let ``TryCatch returns Error when previous result is Error`` () = 411 | let error = Errors.Error("Oh no!") 412 | let result = Result.Error(error) 413 | Result.TryCatch(result, (fun v -> v.ToString()), (fun _ _ -> Errors.Error())) |> should equal (Result.Error(error)) 414 | -------------------------------------------------------------------------------- /Monacs.Core/Option.Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Monacs.Core 6 | { 7 | /// 8 | /// Contains the set of extensions to work with the type. 9 | /// 10 | public static class Option 11 | { 12 | /* Constructors */ 13 | 14 | /// 15 | /// Creates the Some case instance of the type, encapsulating provided value. 16 | /// 17 | /// Type of the encapsulated value. 18 | /// The value to encapsulate. 19 | public static Option Some(T value) => new Option(value); 20 | 21 | /// 22 | /// Creates the None case instance of the type, containing no value. 23 | /// 24 | /// Desired type parameter for type. 25 | public static Option None() => default(Option); 26 | 27 | /* Converters */ 28 | 29 | /// 30 | /// Converts the value of class T to the type. 31 | /// If the value is null, the None case is yielded. 32 | /// Otherwise Some case with provided value is returned. 33 | /// 34 | /// Type of the encapsulated value. 35 | /// The value to convert to . 36 | public static Option OfObject(T value) where T : class => 37 | value != null ? Some(value) : None(); 38 | 39 | /// 40 | /// Converts the value of class T to the type. 41 | /// If the value is null, the None case is yielded. 42 | /// Otherwise Some case with provided value is returned. 43 | /// 44 | /// Extension method variant of 45 | /// Type of the encapsulated value. 46 | /// The value to convert to . 47 | public static Option ToOption(this T value) where T : class => OfObject(value); 48 | 49 | /// 50 | /// Converts the value of to the type. 51 | /// If the value is null, the None case is yielded. 52 | /// Otherwise Some case with provided value is returned. 53 | /// 54 | /// Type of the encapsulated value. 55 | /// The value to convert to . 56 | public static Option OfNullable(T? value) where T : struct => 57 | value.HasValue ? Some(value.Value) : None(); 58 | 59 | /// 60 | /// Converts the value of to the type. 61 | /// If the value is null, the None case is yielded. 62 | /// Otherwise Some case with provided value is returned. 63 | /// 64 | /// Extension method variant of 65 | /// Type of the encapsulated value. 66 | /// The value to convert to . 67 | public static Option ToOption(this T? value) where T : struct => OfNullable(value); 68 | 69 | /// 70 | /// Converts the value of to the type. 71 | /// If the option is the None case, null is yielded. 72 | /// Otherwise encapsulated value is returned. 73 | /// 74 | /// Type of the encapsulated value. 75 | /// The value to convert to . 76 | public static T? ToNullable(Option value) where T : struct => 77 | value.IsSome ? value.Value : (T?)null; 78 | 79 | /// 80 | /// Converts the string value to the type. 81 | /// If the value is null or empty string, the None case is yielded. 82 | /// Otherwise Some case with provided value is returned. 83 | /// 84 | /// The value to convert to . 85 | public static Option OfString(string value) => 86 | string.IsNullOrEmpty(value) ? None() : Some(value); 87 | 88 | /// 89 | /// Converts the string value to the type. 90 | /// If the value is null or empty string, the None case is yielded. 91 | /// Otherwise Some case with provided value is returned. 92 | /// 93 | /// Extension method variant of 94 | /// The value to convert to . 95 | public static Option ToOption(this string value) => OfString(value); 96 | 97 | /// 98 | /// Converts the value of to the type. 99 | /// If the value is the Error case, the None case is yielded. 100 | /// Otherwise Some case with encapsulated value is returned. 101 | /// 102 | /// Type of the encapsulated value. 103 | /// The value to convert to . 104 | public static Option OfResult(Result value) where T : struct => 105 | value.IsOk ? Some(value.Value) : None(); 106 | 107 | /// 108 | /// Converts the value of to the type. 109 | /// If the value is the Error case, the None case is yielded. 110 | /// Otherwise Some case with encapsulated value is returned. 111 | /// 112 | /// Extension method variant of 113 | /// Type of the encapsulated value. 114 | /// The value to convert to . 115 | public static Option ToOption(this Result value) where T : struct => OfResult(value); 116 | 117 | /* TryGetOption */ 118 | 119 | /// 120 | /// Tries to get the element with the given from the . 121 | /// If the value is found, returns Some case of the type with the value from the dictionary. 122 | /// Otherwise returns None case of the type. 123 | /// 124 | /// Type of the key in the dictionary. 125 | /// Type of the value in the dictionary. 126 | /// The dictionary to search in. 127 | /// The key to look for. 128 | public static Option TryGetOption(this IDictionary dictionary, TKey key) => 129 | dictionary.TryGetValue(key, out TValue value) ? Some(value) : None(); 130 | 131 | /// 132 | /// Tries to get the elements with the given from the . 133 | /// If any value is found, returns Some case of the type with the values from the lookup. 134 | /// Otherwise returns None case of the type. 135 | /// 136 | /// Type of the key in the lookup. 137 | /// Type of the value in the lookup. 138 | /// The lookup to search in. 139 | /// The key to look for. 140 | public static Option> TryGetOption(this ILookup lookup, TKey key) => 141 | lookup.Contains(key) ? Some(lookup[key]) : None>(); 142 | 143 | /* Match */ 144 | 145 | /// 146 | /// Does the pattern matching on the type. 147 | /// If the is Some, calls function 148 | /// with the value from the option as a parameter and returns its result. 149 | /// Otherwise calls function and returns its result. 150 | /// 151 | /// Type of the value in the option. 152 | /// Type of the returned value. 153 | /// The option to match on. 154 | /// Function called for the Some case. 155 | /// Function called for the None case. 156 | public static TOut Match(this Option option, Func some, Func none) => 157 | option.IsSome ? some(option.Value) : none(); 158 | 159 | /// 160 | /// Does the pattern matching on the type. 161 | /// If the is Some, returns value. 162 | /// Otherwise returns value. 163 | /// 164 | /// Type of the value in the option. 165 | /// Type of the returned value. 166 | /// The option to match on. 167 | /// Value returned for the Some case. 168 | /// Value returned for the None case. 169 | public static TOut MatchTo(this Option option, TOut some, TOut none) => 170 | option.IsSome ? some : none; 171 | 172 | /* Bind */ 173 | 174 | /// 175 | /// Transforms the into another using the function. 176 | /// If the input option is Some, returns the value of the binder call (which is option). 177 | /// Otherwise returns None case of the option. 178 | /// 179 | /// Type of the value in the input option. 180 | /// Type of the value in the returned option. 181 | /// The option to bind with. 182 | /// Function called with the input option value if it's Some case. 183 | public static Option Bind(this Option option, Func> binder) => 184 | option.IsSome ? binder(option.Value) : None(); 185 | 186 | /* Map */ 187 | 188 | /// 189 | /// Maps the value of the into another using the function. 190 | /// If the input option is Some, returns the Some case with the value of the mapper call (which is ). 191 | /// Otherwise returns None case of the option. 192 | /// 193 | /// Type of the value in the input option. 194 | /// Type of the value in the returned option. 195 | /// The option to map on. 196 | /// Function called with the input option value if it's Some case. 197 | public static Option Map(this Option option, Func mapper) => 198 | option.IsSome ? Some(mapper(option.Value)) : None(); 199 | 200 | /* Getters */ 201 | 202 | /// 203 | /// Gets the value of the if it's Some case. 204 | /// If the option is None case returns value specified by the parameter; 205 | /// if the parameter is not set returns the default value of the type . 206 | /// 207 | /// Type of the value in the option. 208 | /// The option to get a value from. 209 | /// Value to return if the option is the None case. 210 | public static T GetOrDefault(this Option option, T whenNone = default(T)) => 211 | option.IsSome ? option.Value : whenNone; 212 | 213 | /// 214 | /// Gets the value from the using the function if it's Some case. 215 | /// If the option is None case returns value specified by the parameter; 216 | /// if the parameter is not set returns the default value of the type . 217 | /// 218 | /// Effectively the combination of and calls. 219 | /// Type of the value in the option. 220 | /// Type of the return value. 221 | /// The option to get a value from. 222 | /// Function used to get the value if the option is the Some case. 223 | /// Value to return if the option is the None case. 224 | public static TOut GetOrDefault(this Option option, Func getter, TOut whenNone = default(TOut)) => 225 | option.IsSome ? getter(option.Value) : whenNone; 226 | 227 | /* Side Effects */ 228 | 229 | /// 230 | /// Performs the with the value of the if it's Some case. 231 | /// If the option is None case nothing happens. 232 | /// In both cases unmodified option is returned. 233 | /// 234 | /// Type of the value in the option. 235 | /// The option to check for a value. 236 | /// Function executed if the option is Some case. 237 | public static Option Do(this Option option, Action action) 238 | { 239 | if (option.IsSome) 240 | action(option.Value); 241 | return option; 242 | } 243 | 244 | /// 245 | /// Performs the if the is None case. 246 | /// If the option is Some case nothing happens. 247 | /// In both cases unmodified option is returned. 248 | /// 249 | /// Type of the value in the option. 250 | /// The option to check for a value. 251 | /// Function executed if the option is None case. 252 | public static Option DoWhenNone(this Option option, Action action) 253 | { 254 | if (option.IsNone) 255 | action(); 256 | return option; 257 | } 258 | 259 | /* Collections */ 260 | 261 | /// 262 | /// Returns the collection of values of elements from the collection 263 | /// that are Some case (so contain some value). 264 | /// 265 | /// Type of the value in the option. 266 | /// Collection to filter out and map. 267 | public static IEnumerable Choose(this IEnumerable> items) => 268 | items.Where(i => i.IsSome).Select(i => i.Value); 269 | 270 | /// 271 | /// If all elements in the input collection are Some case, returns the Some of the collection of underlying values. 272 | /// Otherwise returns None. 273 | /// 274 | /// Type of the value in the option. 275 | /// Collection to check and map. 276 | public static Option> Sequence(this IEnumerable> items) => 277 | items.Any(i => i.IsNone) 278 | ? None>() 279 | : Some(items.Select(i => i.Value)); 280 | 281 | /// 282 | /// Tries to find the first element of the collection matching the . 283 | /// If element is found, returns Some with the value of that element. 284 | /// Otherwise returns None. 285 | /// 286 | /// Type of the value in the collection and returned option. 287 | /// Collection to search in. 288 | /// Function that checks if given element matches desired condition. 289 | public static Option TryFind(this IEnumerable items, Func predicate) 290 | { 291 | if (items != null) 292 | foreach (T element in items) { 293 | if (predicate(element)) return Some(element); 294 | } 295 | return None(); 296 | } 297 | 298 | /// 299 | /// Tries to get the the element in the collection. 300 | /// If element is found, returns Some with the value of that element. 301 | /// Otherwise returns None. 302 | /// 303 | /// Type of the value in the collection and the returned option. 304 | /// Collection to search in. 305 | public static Option TryFirst(this IEnumerable items) 306 | { 307 | if (items == null) return None(); 308 | IList list = items as IList; 309 | if (list != null) { 310 | if (list.Count > 0) return Some(list[0]); 311 | } else { 312 | foreach (T element in items) { 313 | return Some(element); 314 | } 315 | } 316 | return None(); 317 | } 318 | 319 | /// 320 | /// Tries to get the element of the collection at the posision given by the parameter. 321 | /// If element is found, returns Some with the value of that element. 322 | /// Otherwise returns None. 323 | /// 324 | /// Type of the value in the collection and returned option. 325 | /// Collection to search in. 326 | /// Position at which to look for an element. 327 | public static Option TryElementAt(this IEnumerable items, int index) 328 | { 329 | if (items == null || index < 0) return None(); 330 | IList list = items as IList; 331 | if (list != null) { 332 | if (list.Count >= index+1) return Some(list[index]); 333 | } else { 334 | int idx = 0; 335 | foreach (T element in items) { 336 | if (idx == index) return Some(element); 337 | idx++; 338 | } 339 | } 340 | return None(); 341 | } 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /Monacs.UnitTests/ResultTests.Async.fs: -------------------------------------------------------------------------------- 1 | namespace Monacs.UnitTests.ResultTests.Async 2 | 3 | open System 4 | open System.Threading.Tasks 5 | open FSharp.Control.Tasks 6 | open Xunit 7 | open FsUnit.Xunit 8 | 9 | open Monacs.Core 10 | 11 | [] 12 | module Helpers = 13 | let async v = 14 | v |> Task.FromResult 15 | let asyncF f = 16 | f >> Task.FromResult 17 | let asyncA f a = 18 | f(a) 19 | Task.CompletedTask 20 | let ok v = 21 | Result.Ok(v) 22 | let okAsync v = 23 | v |> Result.Ok |> Task.FromResult 24 | let okF f = 25 | f >> Result.Ok 26 | let okFAsync f = 27 | f >> Result.Ok >> Task.FromResult 28 | 29 | module BindAsync = 30 | 31 | [] 32 | let ``BindAsync with async binder returns result of binder when value is Ok`` () = 33 | task { 34 | let value = ok(42) 35 | let expected = ok("42") 36 | let! result = AsyncResult.BindAsync(value, okFAsync(fun x -> x.ToString())) 37 | result |> should equal expected 38 | } 39 | 40 | [] 41 | let ``BindAsync with async binder returns Error when value is Error`` () = 42 | task { 43 | let error = Errors.Error() 44 | let value = Result.Error(error) 45 | let expected = Result.Error(error) 46 | let! result = AsyncResult.BindAsync(value, okFAsync(fun x -> x.ToString())) 47 | result |> should equal expected 48 | } 49 | 50 | [] 51 | let ``BindAsync with async binder returns result of binder when value is task of Ok`` () = 52 | task { 53 | let value = okAsync(42) 54 | let expected = ok("42") 55 | let! result = AsyncResult.BindAsync(value, okFAsync(fun x -> x.ToString())) 56 | result |> should equal expected 57 | } 58 | 59 | [] 60 | let ``BindAsync with async binder returns Error when value is task of Error`` () = 61 | task { 62 | let error = Errors.Error() 63 | let value = Task.FromResult(Result.Error(error)) 64 | let expected = Result.Error(error) 65 | let! result = AsyncResult.BindAsync(value, okF(fun x -> x.ToString())) 66 | result |> should equal expected 67 | } 68 | 69 | [] 70 | let ``BindAsync returns result of binder when value is task of Ok`` () = 71 | task { 72 | let value = okAsync(42) 73 | let expected = ok("42") 74 | let! result = AsyncResult.BindAsync(value, okF(fun x -> x.ToString())) 75 | result |> should equal expected 76 | } 77 | 78 | [] 79 | let ``BindAsync returns Error when value is task of Error`` () = 80 | task { 81 | let error = Errors.Error() 82 | let value = Task.FromResult(Result.Error(error)) 83 | let expected = Result.Error(error) 84 | let! result = AsyncResult.BindAsync(value, okF(fun x -> x.ToString())) 85 | result |> should equal expected 86 | } 87 | 88 | module MapAsync = 89 | 90 | [] 91 | let ``MapAsync with async mapper returns result of mapper when value is Ok`` () = 92 | task { 93 | let value = ok(42) 94 | let expected = ok("42") 95 | let! result = AsyncResult.MapAsync(value, asyncF(fun x -> x.ToString())) 96 | result |> should equal expected 97 | } 98 | 99 | [] 100 | let ``MapAsync with async mapper returns Error when value is Error`` () = 101 | task { 102 | let error = Errors.Error() 103 | let value = Result.Error(error) 104 | let expected = Result.Error(error) 105 | let! result = AsyncResult.MapAsync(value, asyncF(fun x -> x.ToString())) 106 | result |> should equal expected 107 | } 108 | 109 | [] 110 | let ``BindAsync with async mapper returns result of mapper when value is task of Ok`` () = 111 | task { 112 | let value = okAsync(42) 113 | let expected = ok("42") 114 | let! result = AsyncResult.MapAsync(value, asyncF(fun x -> x.ToString())) 115 | result |> should equal expected 116 | } 117 | 118 | [] 119 | let ``MapAsync with async mapper returns Error when value is task of Error`` () = 120 | task { 121 | let error = Errors.Error() 122 | let value = Task.FromResult(Result.Error(error)) 123 | let expected = Result.Error(error) 124 | let! result = AsyncResult.MapAsync(value, asyncF(fun x -> x.ToString())) 125 | result |> should equal expected 126 | } 127 | 128 | [] 129 | let ``MapAsync returns result of mapper when value is task of Ok`` () = 130 | task { 131 | let value = okAsync(42) 132 | let expected = ok("42") 133 | let! result = AsyncResult.MapAsync(value, (fun x -> x.ToString())) 134 | result |> should equal expected 135 | } 136 | 137 | [] 138 | let ``MapAsync returns Error when value is task of Error`` () = 139 | task { 140 | let error = Errors.Error() 141 | let value = Task.FromResult(Result.Error(error)) 142 | let expected = Result.Error(error) 143 | let! result = AsyncResult.MapAsync(value, (fun x -> x.ToString())) 144 | result |> should equal expected 145 | } 146 | 147 | module MatchAsync = 148 | 149 | let errorMessage (e: ErrorDetails) = e.Message.Value 150 | 151 | [] 152 | let ``MatchAsync returns result of async error when value is Error`` () = 153 | task { 154 | let expected = "test" 155 | let error = Errors.Error(expected) 156 | let value = Result.Error(error) 157 | let! result = AsyncResult.MatchAsync(value, ok = asyncF(fun _ -> ""), error = asyncF(errorMessage)) 158 | result |> should equal expected 159 | } 160 | 161 | [] 162 | let ``MatchAsync returns result of async ok when value is Ok`` () = 163 | task { 164 | let value = ok(42) 165 | let expected = "test" 166 | let! result = AsyncResult.MatchAsync(value, ok = asyncF(fun _ -> expected), error = asyncF(fun _ -> "")) 167 | result |> should equal expected 168 | } 169 | 170 | [] 171 | let ``MatchAsync with sync error returns result of sync error when value is Error`` () = 172 | task { 173 | let expected = "test" 174 | let error = Errors.Error(expected) 175 | let value = Result.Error(error) 176 | let! result = AsyncResult.MatchAsync(value, ok = asyncF(fun _ -> ""), error = errorMessage) 177 | result |> should equal expected 178 | } 179 | 180 | [] 181 | let ``MatchAsync with sync error returns result of async ok when value is Ok`` () = 182 | task { 183 | let value = ok(42) 184 | let expected = "test" 185 | let! result = AsyncResult.MatchAsync(value, ok = asyncF(fun _ -> expected), error = (fun _ -> "")) 186 | result |> should equal expected 187 | } 188 | 189 | [] 190 | let ``MatchAsync with sync ok returns result of async error when value is Error`` () = 191 | task { 192 | let expected = "test" 193 | let error = Errors.Error(expected) 194 | let value = Result.Error(error) 195 | let! result = AsyncResult.MatchAsync(value, ok = (fun _ -> ""), error = asyncF(errorMessage)) 196 | result |> should equal expected 197 | } 198 | 199 | [] 200 | let ``MatchAsync with sync ok returns result of sync ok when value is Ok`` () = 201 | task { 202 | let value = ok(42) 203 | let expected = "test" 204 | let! result = AsyncResult.MatchAsync(value, ok = (fun _ -> expected), error = asyncF(fun _ -> "")) 205 | result |> should equal expected 206 | } 207 | 208 | [] 209 | let ``MatchAsync with sync ok and error returns result of sync error when value is Task>`` () = 210 | task { 211 | let expected = "test" 212 | let error = Errors.Error(expected) 213 | let value = async(Result.Error(error)) 214 | let! result = AsyncResult.MatchAsync(value, ok = (fun _ -> ""), error = errorMessage) 215 | result |> should equal expected 216 | } 217 | 218 | [] 219 | let ``MatchAsync with sync ok and error returns result of sync ok when value is Task>`` () = 220 | task { 221 | let value = async(ok(42)) 222 | let expected = "test" 223 | let! result = AsyncResult.MatchAsync(value, ok = (fun _ -> expected), error = (fun _ -> "")) 224 | result |> should equal expected 225 | } 226 | 227 | [] 228 | let ``MatchAsync with async ok and error returns result of async error when value is Task>`` () = 229 | task { 230 | let expected = "test" 231 | let error = Errors.Error(expected) 232 | let value = async(Result.Error(error)) 233 | let! result = AsyncResult.MatchAsync(value, ok = asyncF(fun _ -> ""), error = asyncF(errorMessage)) 234 | result |> should equal expected 235 | } 236 | 237 | [] 238 | let ``MatchAsync with async ok and error returns result of async ok when value is Task>`` () = 239 | task { 240 | let value = async(ok(42)) 241 | let expected = "test" 242 | let! result = AsyncResult.MatchAsync(value, ok = asyncF(fun _ -> expected), error = asyncF(fun _ -> "")) 243 | result |> should equal expected 244 | } 245 | 246 | [] 247 | let ``MatchAsync with sync ok returns result of async error when value is Task>`` () = 248 | task { 249 | let expected = "test" 250 | let error = Errors.Error(expected) 251 | let value = async(Result.Error(error)) 252 | let! result = AsyncResult.MatchAsync(value, ok = (fun _ -> ""), error = asyncF(errorMessage)) 253 | result |> should equal expected 254 | } 255 | 256 | [] 257 | let ``MatchAsync with sync ok returns result of sync ok when value is Task>`` () = 258 | task { 259 | let value = async(ok(42)) 260 | let expected = "test" 261 | let! result = AsyncResult.MatchAsync(value, ok = (fun _ -> expected), error = asyncF(fun _ -> "")) 262 | result |> should equal expected 263 | } 264 | 265 | [] 266 | let ``MatchAsync with sync error and error returns result of sync error when value is Task>`` () = 267 | task { 268 | let expected = "test" 269 | let error = Errors.Error(expected) 270 | let value = async(Result.Error(error)) 271 | let! result = AsyncResult.MatchAsync(value, ok = asyncF(fun _ -> ""), error = (errorMessage)) 272 | result |> should equal expected 273 | } 274 | 275 | [] 276 | let ``MatchAsync with sync error and error returns result of async ok when value is Task>`` () = 277 | task { 278 | let value = async(ok(42)) 279 | let expected = "test" 280 | let! result = AsyncResult.MatchAsync(value, ok = asyncF(fun _ -> expected), error = (fun _ -> "")) 281 | result |> should equal expected 282 | } 283 | 284 | module ``Side effects`` = 285 | 286 | [] 287 | let ``DoAsync with sync action returns value and executes action when value is Task>`` () = 288 | task { 289 | let value = Result.Ok(42) 290 | let expected = "42" 291 | let mutable result = "" 292 | let! returnValue = AsyncResult.DoAsync(async(value), (fun _ -> result <- expected.ToString())) 293 | returnValue |> should equal value 294 | result |> should equal expected 295 | } 296 | 297 | [] 298 | let ``DoAsync with sync action returns value and doesn't execute action when value is Task>`` () = 299 | task { 300 | let value = Result.Error(Errors.Error()) 301 | let expected = "test" 302 | let mutable result = expected 303 | let! returnValue = AsyncResult.DoAsync(async(value), (fun _ -> result <- "fail")) 304 | returnValue |> should equal value 305 | result |> should equal expected 306 | } 307 | 308 | [] 309 | let ``DoAsync with async action returns value and executes action when value is Ok`` () = 310 | task { 311 | let value = Result.Ok(42) 312 | let expected = "42" 313 | let mutable result = "" 314 | let! returnValue = AsyncResult.DoAsync(value, asyncA(fun _ -> result <- expected.ToString())) 315 | returnValue |> should equal value 316 | result |> should equal expected 317 | } 318 | 319 | [] 320 | let ``DoAsync with async action returns value and doesn't execute action when value is Error`` () = 321 | task { 322 | let value = Result.Error(Errors.Error()) 323 | let expected = "test" 324 | let mutable result = expected 325 | let! returnValue = AsyncResult.DoAsync(value, asyncA(fun _ -> result <- "fail")) 326 | returnValue |> should equal value 327 | result |> should equal expected 328 | } 329 | 330 | [] 331 | let ``DoAsync with async action returns value and executes action when value is Task>`` () = 332 | task { 333 | let value = Result.Ok(42) 334 | let expected = "42" 335 | let mutable result = "" 336 | let! returnValue = AsyncResult.DoAsync(async(value), asyncA(fun _ -> result <- expected.ToString())) 337 | returnValue |> should equal value 338 | result |> should equal expected 339 | } 340 | 341 | [] 342 | let ``DoAsync with async action returns value and doesn't execute action when value is Task>`` () = 343 | task { 344 | let value = Result.Error(Errors.Error()) 345 | let expected = "test" 346 | let mutable result = expected 347 | let! returnValue = AsyncResult.DoAsync(async(value), asyncA(fun _ -> result <- "fail")) 348 | returnValue |> should equal value 349 | result |> should equal expected 350 | } 351 | 352 | [] 353 | let ``DoWhenErrorAsync returns value and doesn't execute action when value is Task>`` () = 354 | task { 355 | let value = Result.Ok(42) 356 | let expected = "test" 357 | let mutable result = expected 358 | let! returnValue = AsyncResult.DoWhenErrorAsync(async(value), (fun _ -> result <- "fail")) 359 | returnValue |> should equal value 360 | result |> should equal expected 361 | } 362 | 363 | [] 364 | let ``DoWhenErrorAsync returns value and executes action when value is Task>`` () = 365 | task { 366 | let value = Result.Error(Errors.Error()) 367 | let expected = "42" 368 | let mutable result = "" 369 | let! returnValue = AsyncResult.DoWhenErrorAsync(async(value), (fun _ -> result <- expected.ToString())) 370 | returnValue |> should equal value 371 | result |> should equal expected 372 | } 373 | 374 | [] 375 | let ``DoWhenErrorAsync with async action returns value and doesn't execute action when value is Ok`` () = 376 | task { 377 | let value = Result.Ok(42) 378 | let expected = "test" 379 | let mutable result = expected 380 | let! returnValue = AsyncResult.DoWhenErrorAsync(value, asyncA(fun _ -> result <- "fail")) 381 | returnValue |> should equal value 382 | result |> should equal expected 383 | } 384 | 385 | [] 386 | let ``DoWhenErrorAsync with async action returns value and executes action when value is Error`` () = 387 | task { 388 | let value = Result.Error(Errors.Error()) 389 | let expected = "42" 390 | let mutable result = "" 391 | let! returnValue = AsyncResult.DoWhenErrorAsync(value, asyncA(fun _ -> result <- expected.ToString())) 392 | returnValue |> should equal value 393 | result |> should equal expected 394 | } 395 | 396 | [] 397 | let ``DoWhenErrorAsync with async action returns value and doesn't execute action when value is Task>`` () = 398 | task { 399 | let value = Result.Ok(42) 400 | let expected = "test" 401 | let mutable result = expected 402 | let! returnValue = AsyncResult.DoWhenErrorAsync(async(value), asyncA(fun _ -> result <- "fail")) 403 | returnValue |> should equal value 404 | result |> should equal expected 405 | } 406 | 407 | [] 408 | let ``DoWhenErrorAsync with async action returns value and executes action when value is Task>`` () = 409 | task { 410 | let value = Result.Error(Errors.Error()) 411 | let expected = "42" 412 | let mutable result = "" 413 | let! returnValue = AsyncResult.DoWhenErrorAsync(async(value), asyncA(fun _ -> result <- expected.ToString())) 414 | returnValue |> should equal value 415 | result |> should equal expected 416 | } 417 | 418 | module TryCatch = 419 | 420 | [] 421 | let ``TryCatchAsync returns Ok when function call doesn't throw`` () = 422 | task { 423 | let value = 42 424 | let! result = AsyncResult.TryCatchAsync((fun () -> async(value)), (fun _ -> Errors.Error())) 425 | result |> should equal (Result.Ok(value)) 426 | } 427 | 428 | [] 429 | let ``TryCatchAsync returns Error when function call throws`` () = 430 | task { 431 | let message = "This should not happen!" 432 | let! result = AsyncResult.TryCatchAsync((fun () -> Task.FromException(Exception(message))), (fun e -> Errors.Error(e.Message))) 433 | result |> should equal (Result.Error(Errors.Error(message))) 434 | } 435 | 436 | [] 437 | let ``TryCatchAsync returns Ok when previous result is Ok and function call doesn't throw`` () = 438 | task { 439 | let value = Result.Ok(42) 440 | let expected = "42" 441 | let! result = AsyncResult.TryCatchAsync(value, (fun v -> async(v.ToString())), (fun _ _ -> Errors.Error())) 442 | result |> should equal (Result.Ok(expected)) 443 | } 444 | 445 | [] 446 | let ``TryCatchAsync returns Error when previous result is Ok and function call throws`` () = 447 | task { 448 | let expected = "OK" 449 | let message = "This should not happen!" 450 | let value = Result.Ok(expected) 451 | let! result = AsyncResult.TryCatchAsync(value, (fun _ -> Task.FromException(Exception(message))), (fun v e -> Errors.Error(v + e.Message))) 452 | result |> should equal (Result.Error(Errors.Error(expected + message))) 453 | } 454 | 455 | [] 456 | let ``TryCatchAsync returns Error when previous result is Error`` () = 457 | task { 458 | let error = Errors.Error("Oh no!") 459 | let value = Result.Error(error) 460 | let! result = AsyncResult.TryCatchAsync(value, (fun v -> async(v.ToString())), (fun _ _ -> Errors.Error())) 461 | result |> should equal (Result.Error(error)) 462 | } -------------------------------------------------------------------------------- /Monacs.Core/Result.Extensions.Async.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using static Monacs.Core.Result; 4 | 5 | namespace Monacs.Core 6 | { 7 | /// 8 | /// Contains the set of async extensions to work with the type. 9 | /// 10 | public static class AsyncResult 11 | { 12 | /* BindAsync */ 13 | 14 | /// 15 | /// Transforms the into another using the function. 16 | /// If the input result is Ok, returns the value of the binder call (which is of ). 17 | /// Otherwise returns Error case of the Result of . 18 | /// 19 | /// Type of the value in the input result. 20 | /// Type of the value in the returned result. 21 | /// The result to bind with. 22 | /// Function called with the input result value if it's Ok case. 23 | public static async Task> BindAsync(this Result result, Func>> binder) => 24 | result.IsOk ? await binder(result.Value) : Error(result.Error); 25 | 26 | /// 27 | /// Transforms the into another using the function. 28 | /// If the input result is Ok, returns the value of the binder call (which is of ). 29 | /// Otherwise returns Error case of the Result of . 30 | /// 31 | /// Type of the value in the input result. 32 | /// Type of the value in the returned result. 33 | /// The result to bind with. 34 | /// Function called with the input result value if it's Ok case. 35 | public static async Task> BindAsync(this Task> result, Func>> binder) => 36 | await (await result).BindAsync(binder); 37 | 38 | /// 39 | /// Transforms the into another using the function. 40 | /// If the input result is Ok, returns the value of the binder call (which is of ). 41 | /// Otherwise returns Error case of the Result of . 42 | /// 43 | /// Type of the value in the input result. 44 | /// Type of the value in the returned result. 45 | /// The result to bind with. 46 | /// Function called with the input result value if it's Ok case. 47 | public static async Task> BindAsync(this Task> result, Func> binder) => 48 | (await result).Bind(binder); 49 | 50 | /* MapAsync */ 51 | 52 | /// 53 | /// Maps the value of the into another using the function. 54 | /// If the input result is Ok, returns the Ok case with the value of the mapper call (which is ). 55 | /// Otherwise returns Error case of the Result of . 56 | /// 57 | /// Type of the value in the input result. 58 | /// Type of the value in the returned result. 59 | /// The result to map on. 60 | /// Function called with the input result value if it's Ok case. 61 | public static async Task> MapAsync(this Result result, Func> mapper) => 62 | (result.IsOk ? Ok(await mapper(result.Value)) : Error(result.Error)); 63 | 64 | /// 65 | /// Maps the value of the into another using the function. 66 | /// If the input result is Ok, returns the Ok case with the value of the mapper call (which is ). 67 | /// Otherwise returns Error case of the Result of . 68 | /// 69 | /// Type of the value in the input result. 70 | /// Type of the value in the returned result. 71 | /// The result to map on. 72 | /// Function called with the input result value if it's Ok case. 73 | public static async Task> MapAsync(this Task> result, Func> mapper) => 74 | await (await result).MapAsync(mapper); 75 | 76 | /// 77 | /// Maps the value of the into another using the function. 78 | /// If the input result is Ok, returns the Ok case with the value of the mapper call (which is ). 79 | /// Otherwise returns Error case of the Result of . 80 | /// 81 | /// Type of the value in the input result. 82 | /// Type of the value in the returned result. 83 | /// The result to map on. 84 | /// Function called with the input result value if it's Ok case. 85 | public static async Task> MapAsync(this Task> result, Func mapper) => 86 | (await result).Map(mapper); 87 | 88 | /* MatchAsync */ 89 | 90 | /// 91 | /// Does the pattern matching on the type. 92 | /// If the is Ok, calls function 93 | /// with the value from the result as a parameter and returns its result. 94 | /// Otherwise calls function and returns its result. 95 | /// 96 | /// Type of the value in the result. 97 | /// Type of the returned value. 98 | /// The result to match on. 99 | /// Function called for the Ok case. 100 | /// Function called for the Error case. 101 | public static async Task MatchAsync(this Result result, Func> ok, Func> error) => 102 | result.IsOk ? await ok(result.Value) : await error(result.Error); 103 | 104 | /// 105 | /// Does the pattern matching on the type. 106 | /// If the is Ok, calls function 107 | /// with the value from the result as a parameter and returns its result. 108 | /// Otherwise calls function and returns its result. 109 | /// 110 | /// Type of the value in the result. 111 | /// Type of the returned value. 112 | /// The result to match on. 113 | /// Function called for the Ok case. 114 | /// Function called for the Error case. 115 | public static async Task MatchAsync(this Result result, Func> ok, Func error) => 116 | result.IsOk ? await ok(result.Value) : error(result.Error); 117 | 118 | /// 119 | /// Does the pattern matching on the type. 120 | /// If the is Ok, calls function 121 | /// with the value from the result as a parameter and returns its result. 122 | /// Otherwise calls function and returns its result. 123 | /// 124 | /// Type of the value in the result. 125 | /// Type of the returned value. 126 | /// The result to match on. 127 | /// Function called for the Ok case. 128 | /// Function called for the Error case. 129 | public static async Task MatchAsync(this Result result, Func ok, Func> error) => 130 | result.IsOk ? ok(result.Value) : await error(result.Error); 131 | 132 | /// 133 | /// Does the pattern matching on the type. 134 | /// If the is Ok, calls function 135 | /// with the value from the result as a parameter and returns its result. 136 | /// Otherwise calls function and returns its result. 137 | /// 138 | /// Type of the value in the result. 139 | /// Type of the returned value. 140 | /// The result to match on. 141 | /// Function called for the Ok case. 142 | /// Function called for the Error case. 143 | public static async Task MatchAsync(this Task> result, Func ok, Func error) => 144 | (await result).Match(ok, error); 145 | 146 | /// 147 | /// Does the pattern matching on the type. 148 | /// If the is Ok, calls function 149 | /// with the value from the result as a parameter and returns its result. 150 | /// Otherwise calls function and returns its result. 151 | /// 152 | /// Type of the value in the result. 153 | /// Type of the returned value. 154 | /// The result to match on. 155 | /// Function called for the Ok case. 156 | /// Function called for the Error case. 157 | public static async Task MatchAsync(this Task> result, Func> ok, Func> error) => 158 | await (await result).MatchAsync(ok, error); 159 | 160 | /// 161 | /// Does the pattern matching on the type. 162 | /// If the is Ok, calls function 163 | /// with the value from the result as a parameter and returns its result. 164 | /// Otherwise calls function and returns its result. 165 | /// 166 | /// Type of the value in the result. 167 | /// Type of the returned value. 168 | /// The result to match on. 169 | /// Function called for the Ok case. 170 | /// Function called for the Error case. 171 | public static async Task MatchAsync(this Task> result, Func> ok, Func error) => 172 | await (await result).MatchAsync(ok, error); 173 | 174 | /// 175 | /// Does the pattern matching on the type. 176 | /// If the is Ok, calls function 177 | /// with the value from the result as a parameter and returns its result. 178 | /// Otherwise calls function and returns its result. 179 | /// 180 | /// Type of the value in the result. 181 | /// Type of the returned value. 182 | /// The result to match on. 183 | /// Function called for the Ok case. 184 | /// Function called for the Error case. 185 | public static async Task MatchAsync(this Task> result, Func ok, Func> error) => 186 | await (await result).MatchAsync(ok, error); 187 | 188 | /* IgnoreAsync */ 189 | 190 | /// 191 | /// Rejects the value of the and returns instead. 192 | /// If the input is Error then the error details are preserved. 193 | /// 194 | /// Type of the encapsulated value. 195 | /// The result of which the value should be ignored. 196 | public static async Task> IgnoreAsync(this Task> result) => 197 | (await result).Map(_ => Unit.Unit.Default); 198 | 199 | /* Side Effects */ 200 | 201 | /// 202 | /// Performs the with the value of the if it's Ok case. 203 | /// If the result is Error case nothing happens. 204 | /// In both cases unmodified result is returned. 205 | /// 206 | /// Type of the value in the result. 207 | /// The result to check for a value. 208 | /// Function executed if the result is Ok case. 209 | public static async Task> DoAsync(this Task> result, Action action) => 210 | (await result).Do(action); 211 | 212 | /// 213 | /// Performs the with the value of the if it's Ok case. 214 | /// If the result is Error case nothing happens. 215 | /// In both cases unmodified result is returned. 216 | /// 217 | /// Type of the value in the result. 218 | /// The result to check for a value. 219 | /// Function executed if the result is Ok case. 220 | public static async Task> DoAsync(this Result result, Func action) 221 | { 222 | if (result.IsOk) 223 | await action(result.Value); 224 | return result; 225 | } 226 | 227 | /// 228 | /// Performs the with the value of the if it's Ok case. 229 | /// If the result is Error case nothing happens. 230 | /// In both cases unmodified result is returned. 231 | /// 232 | /// Type of the value in the result. 233 | /// The result to check for a value. 234 | /// Function executed if the result is Ok case. 235 | public static async Task> DoAsync(this Task> result, Func action) => 236 | await (await result).DoAsync(action); 237 | 238 | /// 239 | /// Performs the if the is Error case. 240 | /// If the result is Ok case nothing happens. 241 | /// In both cases unmodified result is returned. 242 | /// 243 | /// Type of the value in the result. 244 | /// The result to check for a value. 245 | /// Function executed if the result is Error case. 246 | public static async Task> DoWhenErrorAsync(this Task> result, Action action) => 247 | (await result).DoWhenError(action); 248 | 249 | /// 250 | /// Performs the if the is Error case. 251 | /// If the result is Ok case nothing happens. 252 | /// In both cases unmodified result is returned. 253 | /// 254 | /// Type of the value in the result. 255 | /// The result to check for a value. 256 | /// Function executed if the result is Error case. 257 | public static async Task> DoWhenErrorAsync(this Result result, Func action) 258 | { 259 | if (result.IsError) 260 | await action(result.Error); 261 | return result; 262 | } 263 | 264 | /// 265 | /// Performs the if the is Error case. 266 | /// If the result is Ok case nothing happens. 267 | /// In both cases unmodified result is returned. 268 | /// 269 | /// Type of the value in the result. 270 | /// The result to check for a value. 271 | /// Function executed if the result is Error case. 272 | public static async Task> DoWhenErrorAsync(this Task> result, Func action) => 273 | await (await result).DoWhenErrorAsync(action); 274 | 275 | /* Flip */ 276 | 277 | /// 278 | /// Transforms with async value inside to of the result, 279 | /// preserving original result's state and value. 280 | /// 281 | /// Type of the value in the result. 282 | /// Result to take the value from. 283 | public static async Task> FlipAsync(this Result> result) => 284 | result.IsOk 285 | ? Ok(await result.Value) 286 | : Error(result.Error); 287 | 288 | /* TryCatchAsync */ 289 | 290 | /// 291 | /// Tries to execute . 292 | /// If the execution completes without exception, returns Ok with the function result. 293 | /// Otherwise returns Error with details generated by based on the thrown exception. 294 | /// 295 | /// Type of the value in the result. 296 | /// Function to execute. 297 | /// Function that generates error details in case of exception. 298 | public static async Task> TryCatchAsync(Func> func, Func errorHandler) 299 | { 300 | try 301 | { 302 | var result = await func(); 303 | return Ok(result); 304 | } 305 | catch (Exception ex) 306 | { 307 | return Error(errorHandler(ex)); 308 | } 309 | } 310 | 311 | /// 312 | /// Tries to execute with the value from the as an input. 313 | /// If the execution completes without exception, returns Ok with the function result. 314 | /// Otherwise returns Error with details generated by based on the thrown exception. 315 | /// If the is Error function is not executed and the Error is returned. 316 | /// 317 | /// Type of the value in the input result. 318 | /// Type of the value in the output result. 319 | /// Result to take the value from. 320 | /// Function to execute. 321 | /// Function that generates error details in case of exception. 322 | public static async Task> TryCatchAsync(this Result result, Func> func, Func errorHandler) => 323 | await result.BindAsync(value => TryCatchAsync(() => func(value), e => errorHandler(value, e))); 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /Monacs.Core/Result.Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Monacs.Core 6 | { 7 | /// 8 | /// Contains the set of extensions to work with the type. 9 | /// 10 | public static partial class Result 11 | { 12 | /* Constructors */ 13 | 14 | /// 15 | /// Creates the Ok case instance of the type, encapsulating provided value. 16 | /// 17 | /// Type of the encapsulated value. 18 | /// The value to encapsulate. 19 | public static Result Ok(T value) => new Result(value); 20 | 21 | /// 22 | /// Creates the Error case instance of the type, containing error instead of value. 23 | /// 24 | /// Desired type parameter for type. 25 | /// Details of the error. 26 | public static Result Error(ErrorDetails error) => new Result(error); 27 | 28 | /* Converters */ 29 | 30 | /// 31 | /// Converts the value of class T to the type. 32 | /// If the value is null, the Error case is yielded. 33 | /// Otherwise Ok case with provided value is returned. 34 | /// 35 | /// Type of the encapsulated value. 36 | /// The value to convert to . 37 | /// Details of the error if the value is null. 38 | public static Result OfObject(T value, ErrorDetails error) where T : class => 39 | value != null ? Ok(value) : Error(error); 40 | 41 | /// 42 | /// Converts the value of class T to the type. 43 | /// If the value is null, the Error case is yielded. 44 | /// Otherwise Ok case with provided value is returned. 45 | /// 46 | /// Type of the encapsulated value. 47 | /// The value to convert to . 48 | /// Function yielding details of the error if the value is null. 49 | public static Result OfObject(T value, Func errorFunc) where T : class => 50 | value != null ? Ok(value) : Error(errorFunc()); 51 | 52 | /// 53 | /// Converts the value of class T to the type. 54 | /// If the value is null, the Error case is yielded. 55 | /// Otherwise Ok case with provided value is returned. 56 | /// 57 | /// Extension method variant of 58 | /// Type of the encapsulated value. 59 | /// The value to convert to . 60 | /// Details of the error if the value is null. 61 | public static Result ToResult(this T value, ErrorDetails error) where T : class => OfObject(value, error); 62 | 63 | /// 64 | /// Converts the value of class T to the type. 65 | /// If the value is null, the Error case is yielded. 66 | /// Otherwise Ok case with provided value is returned. 67 | /// 68 | /// Extension method variant of 69 | /// Type of the encapsulated value. 70 | /// The value to convert to . 71 | /// Function yielding details of the error if the value is null. 72 | public static Result ToResult(this T value, Func errorFunc) where T : class => OfObject(value, errorFunc); 73 | 74 | /// 75 | /// Converts the value of to the type. 76 | /// If the value is null, the Error case is yielded. 77 | /// Otherwise Ok case with provided value is returned. 78 | /// 79 | /// Type of the encapsulated value. 80 | /// The value to convert to . 81 | /// Details of the error if the value is null. 82 | public static Result OfNullable(T? value, ErrorDetails error) where T : struct => 83 | value.HasValue ? Ok(value.Value) : Error(error); 84 | 85 | /// 86 | /// Converts the value of to the type. 87 | /// If the value is null, the Error case is yielded. 88 | /// Otherwise Ok case with provided value is returned. 89 | /// 90 | /// Type of the encapsulated value. 91 | /// The value to convert to . 92 | /// Function yielding details of the error if the value is null. 93 | public static Result OfNullable(T? value, Func errorFunc) where T : struct => 94 | value.HasValue ? Ok(value.Value) : Error(errorFunc()); 95 | 96 | /// 97 | /// Converts the value of to the type. 98 | /// If the value is null, the Error case is yielded. 99 | /// Otherwise Ok case with provided value is returned. 100 | /// 101 | /// Extension method variant of 102 | /// Type of the encapsulated value. 103 | /// The value to convert to . 104 | /// Details of the error if the value is null. 105 | public static Result ToResult(this T? value, ErrorDetails error) where T : struct => OfNullable(value, error); 106 | 107 | /// 108 | /// Converts the value of to the type. 109 | /// If the value is null, the Error case is yielded. 110 | /// Otherwise Ok case with provided value is returned. 111 | /// 112 | /// Extension method variant of 113 | /// Type of the encapsulated value. 114 | /// The value to convert to . 115 | /// Function yielding details of the error if the value is null. 116 | public static Result ToResult(this T? value, Func errorFunc) where T : struct => OfNullable(value, errorFunc); 117 | 118 | /// 119 | /// Converts the string value to the type. 120 | /// If the value is null or empty string, the Error case is yielded. 121 | /// Otherwise Ok case with provided value is returned. 122 | /// 123 | /// The value to convert to . 124 | /// Details of the error if the value is null or empty. 125 | public static Result OfString(string value, ErrorDetails error) => 126 | string.IsNullOrEmpty(value) ? Error(error) : Ok(value); 127 | 128 | /// 129 | /// Converts the string value to the type. 130 | /// If the value is null or empty string, the Error case is yielded. 131 | /// Otherwise Ok case with provided value is returned. 132 | /// 133 | /// The value to convert to . 134 | /// Function yielding details of the error if the value is null or empty. 135 | public static Result OfString(string value, Func errorFunc) => 136 | string.IsNullOrEmpty(value) ? Error(errorFunc()) : Ok(value); 137 | 138 | /// 139 | /// Converts the string value to the type. 140 | /// If the value is null or empty string, the Error case is yielded. 141 | /// Otherwise Ok case with provided value is returned. 142 | /// 143 | /// Extension method variant of 144 | /// The value to convert to . 145 | /// Details of the error if the value is null or empty. 146 | public static Result ToResult(this string value, ErrorDetails error) => OfString(value, error); 147 | 148 | /// 149 | /// Converts the string value to the type. 150 | /// If the value is null or empty string, the Error case is yielded. 151 | /// Otherwise Ok case with provided value is returned. 152 | /// 153 | /// Extension method variant of 154 | /// The value to convert to . 155 | /// Function yielding details of the error if the value is null or empty. 156 | public static Result ToResult(this string value, Func errorFunc) => OfString(value, errorFunc); 157 | 158 | /// 159 | /// Converts the value of to the type. 160 | /// If the value is None case, the Error case is yielded. 161 | /// Otherwise Ok case with provided value is returned. 162 | /// 163 | /// Type of the encapsulated value. 164 | /// The value to convert to . 165 | /// Details of the error if the value is None. 166 | public static Result OfOption(Option value, ErrorDetails error) where T : struct => 167 | value.IsSome ? Ok(value.Value) : Error(error); 168 | 169 | /// 170 | /// Converts the value of to the type. 171 | /// If the value is None case, the Error case is yielded. 172 | /// Otherwise Ok case with provided value is returned. 173 | /// 174 | /// Type of the encapsulated value. 175 | /// The value to convert to . 176 | /// Function yielding details of the error if the value is None. 177 | public static Result OfOption(Option value, Func errorFunc) where T : struct => 178 | value.IsSome ? Ok(value.Value) : Error(errorFunc()); 179 | 180 | /// 181 | /// Converts the value of to the type. 182 | /// If the value is None case, the Error case is yielded. 183 | /// Otherwise Ok case with provided value is returned. 184 | /// 185 | /// Extension method variant of 186 | /// Type of the encapsulated value. 187 | /// The value to convert to . 188 | /// Details of the error if the value is None. 189 | public static Result ToResult(this Option value, ErrorDetails error) where T : struct => OfOption(value, error); 190 | 191 | /// 192 | /// Converts the value of to the type. 193 | /// If the value is None case, the Error case is yielded. 194 | /// Otherwise Ok case with provided value is returned. 195 | /// 196 | /// Extension method variant of 197 | /// Type of the encapsulated value. 198 | /// The value to convert to . 199 | /// Function yielding details of the error if the value is None. 200 | public static Result ToResult(this Option value, Func errorFunc) where T : struct => OfOption(value, errorFunc); 201 | 202 | /* TryGetResult */ 203 | 204 | /// 205 | /// Tries to get the element with the given from the . 206 | /// If the value is found, returns Ok case of the type with the value from the dictionary. 207 | /// Otherwise returns Error case of the type. 208 | /// 209 | /// Type of the key in the dictionary. 210 | /// Type of the value in the dictionary. 211 | /// The dictionary to search in. 212 | /// The key to look for. 213 | /// Details of the error if the key is not found. 214 | public static Result TryGetResult(this IDictionary dictionary, TKey key, ErrorDetails error) => 215 | dictionary.TryGetValue(key, out TValue value) ? Ok(value) : Error(error); 216 | 217 | /// 218 | /// Tries to get the element with the given from the . 219 | /// If the value is found, returns Ok case of the type with the value from the dictionary. 220 | /// Otherwise returns Error case of the type. 221 | /// 222 | /// Type of the key in the dictionary. 223 | /// Type of the value in the dictionary. 224 | /// The dictionary to search in. 225 | /// The key to look for. 226 | /// Function yielding details of the error if the key is not found. 227 | public static Result TryGetResult(this IDictionary dictionary, TKey key, Func errorFunc) => 228 | dictionary.TryGetValue(key, out TValue value) ? Ok(value) : Error(errorFunc(key)); 229 | 230 | /// 231 | /// Tries to get the elements with the given from the . 232 | /// If any value is found, returns Ok case of the type with the values from the lookup. 233 | /// Otherwise returns Error case of the type. 234 | /// 235 | /// Type of the key in the lookup. 236 | /// Type of the value in the lookup. 237 | /// The lookup to search in. 238 | /// The key to look for. 239 | /// Details of the error if the key is not found. 240 | public static Result> TryGetResult(this ILookup lookup, TKey key, ErrorDetails error) => 241 | lookup.Contains(key) ? Ok(lookup[key]) : Error>(error); 242 | 243 | /// 244 | /// Tries to get the elements with the given from the . 245 | /// If any value is found, returns Ok case of the type with the values from the lookup. 246 | /// Otherwise returns Error case of the type. 247 | /// 248 | /// Type of the key in the lookup. 249 | /// Type of the value in the lookup. 250 | /// The lookup to search in. 251 | /// The key to look for. 252 | /// Function yielding details of the error if the key is not found. 253 | public static Result> TryGetResult(this ILookup lookup, TKey key, Func errorFunc) => 254 | lookup.Contains(key) ? Ok(lookup[key]) : Error>(errorFunc(key)); 255 | 256 | /* Match */ 257 | 258 | /// 259 | /// Does the pattern matching on the type. 260 | /// If the is Ok, calls function 261 | /// with the value from the result as a parameter and returns its result. 262 | /// Otherwise calls function and returns its result. 263 | /// 264 | /// Type of the value in the result. 265 | /// Type of the returned value. 266 | /// The result to match on. 267 | /// Function called for the Ok case. 268 | /// Function called for the Error case. 269 | public static TOut Match(this Result result, Func ok, Func error) => 270 | result.IsOk ? ok(result.Value) : error(result.Error); 271 | 272 | /// 273 | /// Does the pattern matching on the type. 274 | /// If the is Ok, returns value. 275 | /// Otherwise returns value. 276 | /// 277 | /// Type of the value in the result. 278 | /// Type of the returned value. 279 | /// The result to match on. 280 | /// Value returned for the Ok case. 281 | /// Value returned for the Error case. 282 | public static TOut MatchTo(this Result result, TOut ok, TOut error) => 283 | result.IsOk ? ok : error; 284 | 285 | /* Bind */ 286 | 287 | /// 288 | /// Transforms the into another using the function. 289 | /// If the input result is Ok, returns the value of the binder call (which is of ). 290 | /// Otherwise returns Error case of the Result of . 291 | /// 292 | /// Type of the value in the input result. 293 | /// Type of the value in the returned result. 294 | /// The result to bind with. 295 | /// Function called with the input result value if it's Ok case. 296 | public static Result Bind(this Result result, Func> binder) => 297 | result.IsOk ? binder(result.Value) : Error(result.Error); 298 | 299 | /* Map */ 300 | 301 | /// 302 | /// Maps the value of the into another using the function. 303 | /// If the input result is Ok, returns the Ok case with the value of the mapper call (which is ). 304 | /// Otherwise returns Error case of the Result of . 305 | /// 306 | /// Type of the value in the input result. 307 | /// Type of the value in the returned result. 308 | /// The result to map on. 309 | /// Function called with the input result value if it's Ok case. 310 | public static Result Map(this Result result, Func mapper) => 311 | result.IsOk ? Ok(mapper(result.Value)) : Error(result.Error); 312 | 313 | /* Getters */ 314 | 315 | /// 316 | /// Gets the value of the if it's Ok case. 317 | /// If the result is Error case returns value specified by the parameter; 318 | /// if the parameter is not set returns the default value of the type . 319 | /// 320 | /// Type of the value in the result. 321 | /// The result to get a value from. 322 | /// Value to return if the result is the Error case. 323 | public static T GetOrDefault(this Result result, T whenError = default(T)) => 324 | result.IsOk ? result.Value : whenError; 325 | 326 | /// 327 | /// Gets the value from the using the function if it's Ok case. 328 | /// If the result is Error case returns value specified by the parameter; 329 | /// if the parameter is not set returns the default value of the type . 330 | /// 331 | /// Effectively the combination of and calls. 332 | /// Type of the value in the result. 333 | /// Type of the return value. 334 | /// The result to get a value from. 335 | /// Function used to get the value if the result is the Ok case. 336 | /// Value to return if the result is the Error case. 337 | public static TOut GetOrDefault(this Result result, Func getter, TOut whenError = default(TOut)) => 338 | result.IsOk ? getter(result.Value) : whenError; 339 | 340 | /* Side Effects */ 341 | 342 | /// 343 | /// Performs the with the value of the if it's Ok case. 344 | /// If the result is Error case nothing happens. 345 | /// In both cases unmodified result is returned. 346 | /// 347 | /// Type of the value in the result. 348 | /// The result to check for a value. 349 | /// Function executed if the result is Ok case. 350 | public static Result Do(this Result result, Action action) 351 | { 352 | if (result.IsOk) 353 | action(result.Value); 354 | return result; 355 | } 356 | 357 | /// 358 | /// Performs the if the is Error case. 359 | /// If the result is Ok case nothing happens. 360 | /// In both cases unmodified result is returned. 361 | /// 362 | /// Type of the value in the result. 363 | /// The result to check for a value. 364 | /// Function executed if the result is Error case. 365 | public static Result DoWhenError(this Result result, Action action) 366 | { 367 | if (result.IsError) 368 | action(result.Error); 369 | return result; 370 | } 371 | 372 | /* Collections */ 373 | 374 | /// 375 | /// Returns the collection of values of elements from the collection 376 | /// that are Ok case. 377 | /// 378 | /// Type of the value in the result. 379 | /// Collection to filter out and map. 380 | public static IEnumerable Choose(this IEnumerable> items) => 381 | items.Where(i => i.IsOk).Select(i => i.Value); 382 | 383 | /// 384 | /// Returns the collection of values of elements from the collection 385 | /// that are Error case. 386 | /// 387 | /// Type of the value in the result. 388 | /// Collection to filter out and map. 389 | public static IEnumerable ChooseErrors(this IEnumerable> items) => 390 | items.Where(r => r.IsError).Select(x => x.Error); 391 | 392 | /// 393 | /// If all elements in the input collection are Ok case, returns the Ok of the collection of underlying values. 394 | /// Otherwise returns Error from the first element. 395 | /// 396 | /// Type of the value in the result. 397 | /// Collection to check and map. 398 | public static Result> Sequence(this IEnumerable> items) => 399 | items.Any(i => i.IsError) 400 | ? Error>(items.First(i => i.IsError).Error) 401 | : Ok(items.Select(i => i.Value)); 402 | 403 | /* TryCatch */ 404 | 405 | /// 406 | /// Tries to execute . 407 | /// If the execution completes without exception, returns Ok with the function result. 408 | /// Otherwise returns Error with details generated by based on the thrown exception. 409 | /// 410 | /// Type of the value in the result. 411 | /// Function to execute. 412 | /// Function that generates error details in case of exception. 413 | public static Result TryCatch(Func func, Func errorHandler) 414 | { 415 | try 416 | { 417 | var result = func(); 418 | return Ok(result); 419 | } 420 | catch (Exception ex) 421 | { 422 | return Error(errorHandler(ex)); 423 | } 424 | } 425 | 426 | /// 427 | /// Tries to execute with the value from the as an input. 428 | /// If the execution completes without exception, returns Ok with the function result. 429 | /// Otherwise returns Error with details generated by based on the thrown exception. 430 | /// If the is Error function is not executed and the Error is returned. 431 | /// 432 | /// Type of the value in the input result. 433 | /// Type of the value in the output result. 434 | /// Result to take the value from. 435 | /// Function to execute. 436 | /// Function that generates error details in case of exception. 437 | public static Result TryCatch(this Result result, Func func, Func errorHandler) => 438 | result.Bind(value => Result.TryCatch(() => func(value), e => errorHandler(value, e))); 439 | } 440 | } --------------------------------------------------------------------------------