Skip to content

Instantly share code, notes, and snippets.

@AlexeyRaga
Created January 12, 2022 10:35
Show Gist options
  • Save AlexeyRaga/fd7e0dea26e73b6d3d2addd24c055cad to your computer and use it in GitHub Desktop.
Save AlexeyRaga/fd7e0dea26e73b6d3d2addd24c055cad to your computer and use it in GitHub Desktop.
Either for dotnet
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
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
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