Skip to content

Commit

Permalink
Merge pull request #47 from messerli-informatik-ag/split-option
Browse files Browse the repository at this point in the history
Split functions on Option into multiple files
  • Loading branch information
FreeApophis authored Jun 10, 2020
2 parents 48fa4ab + 40151e9 commit 5d96adb
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 167 deletions.
167 changes: 0 additions & 167 deletions Funcky/Monads/Option.cs

This file was deleted.

45 changes: 45 additions & 0 deletions Funcky/Monads/Option/Option.Convenience.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static Funcky.Functional;

namespace Funcky.Monads
{
public readonly partial struct Option<TItem>
{
public Option<TItem> Where(Func<TItem, bool> predicate)
=> SelectMany(item => predicate(item) ? Option.Some(item) : None());

public Option<TItem> OrElse(Option<TItem> elseOption)
=> Match(none: elseOption, some: Option.Some);

public TItem OrElse(TItem elseOption)
=> Match(none: elseOption, some: Identity);

public Option<TItem> OrElse(Func<Option<TItem>> elseOption)
=> Match(none: elseOption, some: Option.Some);

public TItem OrElse(Func<TItem> elseOption)
=> Match(none: elseOption, some: Identity);

public Option<TResult> AndThen<TResult>(Func<TItem, TResult> andThenFunction)
where TResult : notnull
=> Select(andThenFunction);

public Option<TResult> AndThen<TResult>(Func<TItem, Option<TResult>> andThenFunction)
where TResult : notnull
=> SelectMany(andThenFunction);

public void AndThen(Action<TItem> andThenFunction)
=> Match(none: NoOperation, some: andThenFunction);

/// <summary>
/// Returns an <see cref="IEnumerable{T}"/> that yields exactly one value when the option
/// has an item and nothing when the option is empty.
/// </summary>
public IEnumerable<TItem> ToEnumerable()
=> Match(
none: Enumerable.Empty<TItem>(),
some: value => Enumerable.Repeat(value, 1));
}
}
93 changes: 93 additions & 0 deletions Funcky/Monads/Option/Option.Core.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Funcky.GenericConstraints;

namespace Funcky.Monads
{
public readonly partial struct Option<TItem> : IToString
where TItem : notnull
{
private readonly bool _hasItem;
private readonly TItem _item;

internal Option(TItem item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}

_item = item;
_hasItem = true;
}

public static bool operator ==(Option<TItem> lhs, Option<TItem> rhs) => lhs.Equals(rhs);

public static bool operator !=(Option<TItem> lhs, Option<TItem> rhs) => !lhs.Equals(rhs);

public static Option<TItem> None() => default;

public Option<TResult> Select<TResult>(Func<TItem, TResult> selector)
where TResult : notnull
=> Match(
none: Option<TResult>.None,
item => Option.Some(selector(item)));

public Option<TResult> SelectMany<TResult>(Func<TItem, Option<TResult>> selector)
where TResult : notnull
=> SelectMany(selector, (_, result) => result);

public Option<TResult> SelectMany<TMaybe, TResult>(Func<TItem, Option<TMaybe>> maybeSelector, Func<TItem, TMaybe, TResult> resultSelector)
where TResult : notnull
where TMaybe : notnull
=> Match(
none: Option<TResult>.None,
some: item => maybeSelector(item).Select(
maybe => resultSelector(item, maybe)));

public TResult Match<TResult>(TResult none, Func<TItem, TResult> some)
=> Match(() => none, some);

public TResult Match<TResult>(Func<TResult> none, Func<TItem, TResult> some)
=> _hasItem
? some(_item)
: none();

public void Match(Action none, Action<TItem> some)
{
if (_hasItem)
{
some(_item);
}
else
{
none();
}
}

public override bool Equals(object obj)
=> obj is Option<TItem> other && Equals(_item, other._item);

public override int GetHashCode()
=> Match(
none: 0,
some: item => item.GetHashCode());

public override string ToString()
=> Match(
none: "None",
some: value => $"Some({value})");
}

public static partial class Option
{
public static Option<TItem> Some<TItem>(TItem item)
where TItem : notnull
=> new Option<TItem>(item);

public static Option<TItem> Some<TItem>(Option<TItem> item)
where TItem : notnull
=> item;
}
}
24 changes: 24 additions & 0 deletions Funcky/Monads/Option/Option.FromNullable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Funcky.GenericConstraints;

namespace Funcky.Monads
{
public static partial class Option
{
/// <summary>
/// Creates an <see cref="Option{T}"/> from a nullable value.
/// </summary>
public static Option<T> From<T>(T? item, RequireClass<T>? ω = null)
where T : class
=> item is { } value ? Some(value) : Option<T>.None();

/// <inheritdoc cref="From{T}(T, RequireClass{T})"/>
public static Option<T> From<T>(T item, RequireStruct<T>? ω = null)
where T : struct
=> Some(item);

/// <inheritdoc cref="From{T}(T, RequireClass{T})"/>
public static Option<T> From<T>(T? item)
where T : struct
=> item.HasValue ? Some(item.Value) : Option<T>.None();
}
}
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@
## Unreleased
* Added overload for AndThen which flattens the Option
* Add `Where` method to `Option<T>`, which allows filtering the `Option` by a predicate.
* Add overload for `Option<T>.SelectMany` that takes only a selector.

0 comments on commit 5d96adb

Please sign in to comment.