Skip to content

Commit

Permalink
maybe instantiation simplified on client side (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dirk-Peters authored May 3, 2021
1 parent 3472ef1 commit 3e5c4f4
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 30 deletions.
5 changes: 3 additions & 2 deletions MonadicBits/Either.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using static MonadicBits.Functional;

namespace MonadicBits
{
Expand Down Expand Up @@ -74,6 +75,6 @@ public Either<TResult, TRight> MapLeft<TResult>([NotNull] Func<TLeft, TResult> m
return Match(left => Either<TResult, TRight>.Left(mapping(left)), Either<TResult, TRight>.Right);
}

public Maybe<TRight> ToMaybe() => Match(_ => Maybe<TRight>.Nothing(), Maybe<TRight>.Just);
public Maybe<TRight> ToMaybe() => Match(_ => Nothing, right => right.Just());
}
}
}
36 changes: 24 additions & 12 deletions MonadicBits/Maybe.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
using System;
using System.Diagnostics.CodeAnalysis;
using MonadicBits.Maybe;

namespace MonadicBits
{
using static Functional;

public static partial class Functional
{
public static Nothing Nothing = default;
}

public readonly struct Maybe<T>
{
private T Instance { get; }
Expand All @@ -14,22 +22,14 @@ private Maybe(T instance)
IsJust = true;
}

public static Maybe<T> Just(T instance) => new Maybe<T>(instance);

public static Maybe<T> Nothing() => new Maybe<T>();

public Maybe<TResult> Bind<TResult>([NotNull] Func<T, Maybe<TResult>> mapping)
{
if (mapping == null) throw new ArgumentNullException(nameof(mapping));

return IsJust ? mapping(Instance) : Maybe<TResult>.Nothing();
}
public Maybe<TResult> Bind<TResult>([NotNull] Func<T, Maybe<TResult>> mapping) =>
Match(mapping, () => Nothing);

public Maybe<TResult> Map<TResult>([NotNull] Func<T, TResult> mapping)
{
if (mapping == null) throw new ArgumentNullException(nameof(mapping));

return Match(value => Maybe<TResult>.Just(mapping(value)), Maybe<TResult>.Nothing);
return Match(value => mapping(value).Just(), () => Nothing);
}

public TResult Match<TResult>([NotNull] Func<T, TResult> just, [NotNull] Func<TResult> nothing)
Expand All @@ -53,5 +53,17 @@ public void Match([NotNull] Action<T> just, [NotNull] Action nothing)

public Either<TLeft, T> ToEither<TLeft>([NotNull] TLeft left) =>
IsJust ? Either<TLeft, T>.Right(Instance) : Either<TLeft, T>.Left(left);

public static implicit operator Maybe<T>(Nothing _) => new Maybe<T>();

public static implicit operator Maybe<T>(T value) =>
value == null ? Nothing : new Maybe<T>(value);
}

namespace Maybe
{
public readonly struct Nothing
{
}
}
}
}
5 changes: 3 additions & 2 deletions MonadicBits/MaybeAsyncExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using static MonadicBits.Functional;

namespace MonadicBits
{
Expand All @@ -9,7 +10,7 @@ public static Task<Maybe<TResult>> MapAsync<T, TResult>(this Maybe<T> maybe,
Func<T, Task<TResult>> mapping, bool continueOnCapturedContext = false) =>
maybe.Map(mapping).Match(
async task => (await task.ConfigureAwait(continueOnCapturedContext)).Just(),
() => Task.FromResult(Maybe<TResult>.Nothing()));
() => Task.FromResult<Maybe<TResult>>(Nothing));

public static async Task<Maybe<TResult>> MapAsync<T, TResult>(this Task<Maybe<T>> maybeTask,
Func<T, TResult> mapping, bool continueOnCapturedContext = false) =>
Expand All @@ -27,7 +28,7 @@ public static Task<Maybe<TResult>> BindAsync<T, TResult>(this Maybe<T> maybe,

return maybe.Match(
async value => await mapping(value).ConfigureAwait(continueOnCapturedContext),
() => Task.FromResult(Maybe<TResult>.Nothing()));
() => Task.FromResult<Maybe<TResult>>(Nothing));
}

public static async Task<Maybe<TResult>> BindAsync<T, TResult>(this Task<Maybe<T>> maybeTask,
Expand Down
15 changes: 11 additions & 4 deletions MonadicBits/MaybeExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static MonadicBits.Functional;

namespace MonadicBits
{
public static class MaybeExtensions
{
public static Maybe<T> Just<T>(this T value) => Maybe<T>.Just(value);
public static Maybe<T> Just<T>(this T value)
{
if (value == null) throw new ArgumentNullException(
nameof(value),
"cannot create just from null value");
return value;
}

public static Maybe<T> JustNotNull<T>(this T value) =>
value == null ? Maybe<T>.Nothing() : Maybe<T>.Just(value);
value == null ? Nothing : value.Just();

public static Maybe<T> FirstOrNothing<T>(this IEnumerable<T> source) =>
source.Select(value => value.Just()).DefaultIfEmpty(Maybe<T>.Nothing()).First();
source.Select(value => value.Just()).DefaultIfEmpty(Nothing).First();

public static Maybe<T> JustWhen<T>(this T value, Func<T, bool> predicate)
{
if (predicate == null) throw new ArgumentNullException(nameof(predicate));

return predicate(value) ? Maybe<T>.Just(value) : Maybe<T>.Nothing();
return predicate(value) ? value.Just() : Nothing;
}
}
}
5 changes: 3 additions & 2 deletions MonadicBitsTests/MaybeAsyncExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Tasks;
using MonadicBits;
using NUnit.Framework;
using static MonadicBitsTests.TestMonads;

namespace MonadicBitsTests
{
Expand All @@ -17,7 +18,7 @@ public static async Task MapAsync_maybe_with_value_to_async_mapping_returns_mayb

[Test]
public static async Task MapAsync_empty_maybe_to_async_mapping_returns_empty_maybe_task() =>
(await Maybe<string>.Nothing().MapAsync(_ => Task.FromResult(42)))
(await Nothing<string>().MapAsync(_ => Task.FromResult(42)))
.Match(_ => Assert.Fail(), Assert.Pass);

[Test]
Expand Down Expand Up @@ -51,7 +52,7 @@ public static async Task BindAsync_maybe_with_value_to_async_mapping_returns_may

[Test]
public static async Task BindAsync_empty_maybe_to_async_mapping_empty_maybe_task() =>
(await Maybe<string>.Nothing().BindAsync(_ => Task.FromResult(42.Just())))
(await Nothing<string>().BindAsync(_ => Task.FromResult(42.Just())))
.Match(_ => Assert.Fail(), Assert.Pass);

[Test]
Expand Down
4 changes: 4 additions & 0 deletions MonadicBitsTests/MaybeExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public static void FirstOrNothing_with_list_of_values_returns_maybe_of_first_val
public static void FirstOrNothing_with_empty_list_returns_nothing() =>
new List<int>().FirstOrNothing().Match(_ => Assert.Fail(), Assert.Pass);

[Test]
public static void Just_throws_on_null() =>
Assert.Throws<ArgumentNullException>(() => ((object) null).Just());

[Test]
public static void JustWhen_with_null_predicate_throws_exception() =>
Assert.Throws<ArgumentNullException>(() => "Test".JustWhen(null));
Expand Down
17 changes: 9 additions & 8 deletions MonadicBitsTests/MaybeTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using MonadicBits;
using NUnit.Framework;
using static MonadicBitsTests.TestMonads;

namespace MonadicBitsTests
{
Expand All @@ -10,12 +11,12 @@ public static class MaybeTests
public static void Just_creates_maybe_with_value()
{
const string input = "Test";
Maybe<string>.Just(input).Match(s => Assert.AreEqual(input, s), Assert.Fail);
input.Just().Match(s => Assert.AreEqual(input, s), Assert.Fail);
}

[Test]
public static void Nothing_creates_empty_maybe() =>
Maybe<string>.Nothing().Match(Assert.Fail, Assert.Pass);
Nothing<string>().Match(Assert.Fail, Assert.Pass);

[Test]
public static void Match_with_null_just_action_throws_exception() =>
Expand Down Expand Up @@ -44,16 +45,16 @@ public static void Match_maybe_with_value_returns_value()
public static void Match_empty_maybe_returns_nothing_value()
{
const string value = "Test";
Assert.AreEqual(value, Maybe<string>.Nothing().Match(_ => "Just", () => value));
Assert.AreEqual(value, Nothing<string>().Match(_ => "Just", () => value));
}

[Test]
public static void Match_maybe_with_value_calls_just_action() =>
"Test".Just().Match(Assert.Pass, Assert.Fail);
WithValue().Match(_ => Assert.Pass(), Assert.Fail);

[Test]
public static void Match_empty_maybe_calls_nothing_action() =>
Maybe<string>.Nothing().Match(Assert.Fail, Assert.Pass);
Nothing<string>().Match(Assert.Fail, Assert.Pass);

[Test]
public static void Map_with_null_mapping_throws_exception() =>
Expand All @@ -68,7 +69,7 @@ public static void Map_maybe_with_value_returns_maybe_with_new_type_and_value()

[Test]
public static void Map_empty_maybe_returns_empty_maybe() =>
Maybe<string>.Nothing().Map(_ => 42).Match(_ => Assert.Fail(), Assert.Pass);
Nothing<string>().Map(_ => 42).Match(_ => Assert.Fail(), Assert.Pass);

[Test]
public static void Bind_to_null_method_throws_exception() =>
Expand All @@ -83,7 +84,7 @@ public static void Bind_maybe_with_value_to_method_returns_maybe()

[Test]
public static void Bind_empty_maybe_to_method_returns_empty_maybe() =>
Maybe<string>.Nothing().Bind(_ => 42.Just()).Match(_ => Assert.Fail(), Assert.Pass);
Nothing<string>().Bind(_ => 42.Just()).Match(_ => Assert.Fail(), Assert.Pass);

[Test]
public static void Just_to_either_makes_right()
Expand All @@ -97,7 +98,7 @@ public static void Just_to_either_makes_right()
public static void Nothing_to_either_makes_left()
{
const string leftInput = "Left";
var result = Maybe<string>.Nothing().ToEither(leftInput);
var result = Nothing<string>().ToEither(leftInput);
result.Match(left => Assert.AreEqual(leftInput, left), Assert.Fail);
}
}
Expand Down
11 changes: 11 additions & 0 deletions MonadicBitsTests/TestMonads.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using MonadicBits;

namespace MonadicBitsTests
{
public static class TestMonads
{
public static Maybe<T> Nothing<T>() => Functional.Nothing;

public static Maybe<int> WithValue() => 42;
}
}

0 comments on commit 3e5c4f4

Please sign in to comment.