Created
January 12, 2022 10:35
-
-
Save AlexeyRaga/fd7e0dea26e73b6d3d2addd24c055cad to your computer and use it in GitHub Desktop.
Either for dotnet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Prelude.Core | |
type Either<'TLeft, 'TRight> = Left of 'TLeft | Right of 'TRight | |
module Either = | |
let inline either f g value = | |
match value with | |
| Left value -> f value | |
| Right value -> g value | |
let inline map f value = either Left (f >> Right) value | |
let inline mapLeft f value = either (f >> Left) Right value | |
let inline bimap f g value = either (f >> Left) (g >> Right) value | |
let inline bind f value = either Left f value | |
let inline swap either = | |
match either with | |
| Left value -> Right value | |
| Right value -> Left value | |
let inline fromLeft z value = either id (fun _ -> z) value | |
let inline fromRight z value = either (fun _ -> z) id value | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Prelude.Core.CSharp | |
open System | |
open System.Runtime.CompilerServices | |
open Prelude.Core | |
[<Extension; AbstractClass; Sealed>] | |
type EitherExtensions private () = | |
[<Extension>] | |
static member Select(either : Either<'TLeft, 'TRight>, transform : Func<'TRight, 'TResult>) = | |
Either.map transform.Invoke either | |
[<Extension>] | |
static member SelectLeft(either : Either<'TLeft, 'TRight>, transform : Func<'TLeft, 'TResult>) = | |
Either.mapLeft transform.Invoke either | |
[<Extension>] | |
static member SelectMany(either : Either<'TLeft, 'TRight>, transform : Func<'TRight, Either<'TLeft, 'TResult>>) = | |
Either.bind transform.Invoke either | |
[<Extension>] | |
static member FromLeft(either : Either<'TLeft, 'TRight>, orElse : 'TLeft) = | |
Either.fromLeft orElse either | |
[<Extension>] | |
static member FromRight(either : Either<'TLeft, 'TRight>, orElse : 'TRight) = | |
Either.fromRight orElse either | |
[<Extension>] | |
static member Either(either : Either<'TLeft, 'TRight>, transformLeft : Func<'TLeft, 'TResult>, transformRight : Func<'TRight, 'TResult>) = | |
Either.either transformLeft.Invoke transformRight.Invoke either | |
[<Extension>] | |
static member Swap(either: Either<'TLeft, 'TRight>) = | |
Either.swap either | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Prelude.Core; | |
using Prelude.Core.CSharp; | |
using Hedgehog.Xunit; | |
using FluentAssertions; | |
namespace Prelude.CSharp.Tests | |
{ | |
public sealed class EitherTests | |
{ | |
[Property] | |
public void ShouldMapLeft(byte input) | |
{ | |
Either<int, string>.NewLeft(input) | |
.SelectLeft(x => x + 1) | |
.SelectLeft(x => x * 2) | |
.FromLeft(0) | |
.Should().Be((input + 1) * 2); | |
} | |
[Property] | |
public void ShouldMapRight(byte input) | |
{ | |
Either<string, int>.NewRight(input) | |
.Select(x => x + 1) | |
.Select(x => x * 2) | |
.FromRight(0) | |
.Should().Be((input + 1) * 2); | |
} | |
[Property] | |
public void ShouldBindLeft(byte input, string error) | |
{ | |
var leftValue = Either<string, int>.NewLeft(error); | |
var rightValue = Either<string, int>.NewRight(input); | |
rightValue.SelectMany(x => leftValue).Should().BeEquivalentTo(leftValue); | |
leftValue.SelectMany(x => rightValue).Should().BeEquivalentTo(leftValue); | |
} | |
[Property] | |
public void ShouldBindRight(byte input) | |
{ | |
Either<string, int>.NewRight(input) | |
.SelectMany(x => Either<string, int>.NewRight(x + 1)) | |
.SelectMany(x => Either<string, int>.NewRight(x * 2)) | |
.FromRight(0) | |
.Should().Be((input + 1) * 2); | |
} | |
[Property] | |
public void ShouldSwap(Either<int, string> either) | |
{ | |
var swapped = either.Swap(); | |
swapped.Swap().Should().BeEquivalentTo(either); | |
swapped.FromRight(0).Should().Be(either.FromLeft(0)); | |
swapped.FromLeft("").Should().Be(either.FromRight("")); | |
} | |
[Property] | |
public void ShouldPatternMatch(Either<int, string> either) | |
{ | |
switch (either) | |
{ | |
case Either<int, string>.Left left: | |
left.Item.Should().Be(either.FromLeft(0)); | |
break; | |
case Either<int, string>.Right right: | |
right.Item.Should().Be(either.FromRight("")); | |
break; | |
} | |
} | |
[Property] | |
public void ShouldPatternMatchExpression(Either<int, string> either) | |
{ | |
var result = either switch | |
{ | |
Either<int, string>.Left left => left.Item.ToString(), | |
Either<int, string>.Right right => right.Item, | |
_ => throw new InvalidProgramException("What else can it be?!") | |
}; | |
result.Should().Be(either.Either(x => x.ToString(), x => x)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment