Skip to content

Commit 5d96adb

Browse files
authored
Merge pull request #47 from messerli-informatik-ag/split-option
Split functions on Option into multiple files
2 parents 48fa4ab + 40151e9 commit 5d96adb

File tree

5 files changed

+164
-167
lines changed

5 files changed

+164
-167
lines changed

Funcky/Monads/Option.cs

-167
This file was deleted.
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using static Funcky.Functional;
5+
6+
namespace Funcky.Monads
7+
{
8+
public readonly partial struct Option<TItem>
9+
{
10+
public Option<TItem> Where(Func<TItem, bool> predicate)
11+
=> SelectMany(item => predicate(item) ? Option.Some(item) : None());
12+
13+
public Option<TItem> OrElse(Option<TItem> elseOption)
14+
=> Match(none: elseOption, some: Option.Some);
15+
16+
public TItem OrElse(TItem elseOption)
17+
=> Match(none: elseOption, some: Identity);
18+
19+
public Option<TItem> OrElse(Func<Option<TItem>> elseOption)
20+
=> Match(none: elseOption, some: Option.Some);
21+
22+
public TItem OrElse(Func<TItem> elseOption)
23+
=> Match(none: elseOption, some: Identity);
24+
25+
public Option<TResult> AndThen<TResult>(Func<TItem, TResult> andThenFunction)
26+
where TResult : notnull
27+
=> Select(andThenFunction);
28+
29+
public Option<TResult> AndThen<TResult>(Func<TItem, Option<TResult>> andThenFunction)
30+
where TResult : notnull
31+
=> SelectMany(andThenFunction);
32+
33+
public void AndThen(Action<TItem> andThenFunction)
34+
=> Match(none: NoOperation, some: andThenFunction);
35+
36+
/// <summary>
37+
/// Returns an <see cref="IEnumerable{T}"/> that yields exactly one value when the option
38+
/// has an item and nothing when the option is empty.
39+
/// </summary>
40+
public IEnumerable<TItem> ToEnumerable()
41+
=> Match(
42+
none: Enumerable.Empty<TItem>(),
43+
some: value => Enumerable.Repeat(value, 1));
44+
}
45+
}

Funcky/Monads/Option/Option.Core.cs

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Funcky.GenericConstraints;
5+
6+
namespace Funcky.Monads
7+
{
8+
public readonly partial struct Option<TItem> : IToString
9+
where TItem : notnull
10+
{
11+
private readonly bool _hasItem;
12+
private readonly TItem _item;
13+
14+
internal Option(TItem item)
15+
{
16+
if (item == null)
17+
{
18+
throw new ArgumentNullException(nameof(item));
19+
}
20+
21+
_item = item;
22+
_hasItem = true;
23+
}
24+
25+
public static bool operator ==(Option<TItem> lhs, Option<TItem> rhs) => lhs.Equals(rhs);
26+
27+
public static bool operator !=(Option<TItem> lhs, Option<TItem> rhs) => !lhs.Equals(rhs);
28+
29+
public static Option<TItem> None() => default;
30+
31+
public Option<TResult> Select<TResult>(Func<TItem, TResult> selector)
32+
where TResult : notnull
33+
=> Match(
34+
none: Option<TResult>.None,
35+
item => Option.Some(selector(item)));
36+
37+
public Option<TResult> SelectMany<TResult>(Func<TItem, Option<TResult>> selector)
38+
where TResult : notnull
39+
=> SelectMany(selector, (_, result) => result);
40+
41+
public Option<TResult> SelectMany<TMaybe, TResult>(Func<TItem, Option<TMaybe>> maybeSelector, Func<TItem, TMaybe, TResult> resultSelector)
42+
where TResult : notnull
43+
where TMaybe : notnull
44+
=> Match(
45+
none: Option<TResult>.None,
46+
some: item => maybeSelector(item).Select(
47+
maybe => resultSelector(item, maybe)));
48+
49+
public TResult Match<TResult>(TResult none, Func<TItem, TResult> some)
50+
=> Match(() => none, some);
51+
52+
public TResult Match<TResult>(Func<TResult> none, Func<TItem, TResult> some)
53+
=> _hasItem
54+
? some(_item)
55+
: none();
56+
57+
public void Match(Action none, Action<TItem> some)
58+
{
59+
if (_hasItem)
60+
{
61+
some(_item);
62+
}
63+
else
64+
{
65+
none();
66+
}
67+
}
68+
69+
public override bool Equals(object obj)
70+
=> obj is Option<TItem> other && Equals(_item, other._item);
71+
72+
public override int GetHashCode()
73+
=> Match(
74+
none: 0,
75+
some: item => item.GetHashCode());
76+
77+
public override string ToString()
78+
=> Match(
79+
none: "None",
80+
some: value => $"Some({value})");
81+
}
82+
83+
public static partial class Option
84+
{
85+
public static Option<TItem> Some<TItem>(TItem item)
86+
where TItem : notnull
87+
=> new Option<TItem>(item);
88+
89+
public static Option<TItem> Some<TItem>(Option<TItem> item)
90+
where TItem : notnull
91+
=> item;
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Funcky.GenericConstraints;
2+
3+
namespace Funcky.Monads
4+
{
5+
public static partial class Option
6+
{
7+
/// <summary>
8+
/// Creates an <see cref="Option{T}"/> from a nullable value.
9+
/// </summary>
10+
public static Option<T> From<T>(T? item, RequireClass<T>? ω = null)
11+
where T : class
12+
=> item is { } value ? Some(value) : Option<T>.None();
13+
14+
/// <inheritdoc cref="From{T}(T, RequireClass{T})"/>
15+
public static Option<T> From<T>(T item, RequireStruct<T>? ω = null)
16+
where T : struct
17+
=> Some(item);
18+
19+
/// <inheritdoc cref="From{T}(T, RequireClass{T})"/>
20+
public static Option<T> From<T>(T? item)
21+
where T : struct
22+
=> item.HasValue ? Some(item.Value) : Option<T>.None();
23+
}
24+
}

changelog.md

+2
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@
1616
## Unreleased
1717
* Added overload for AndThen which flattens the Option
1818
* Add `Where` method to `Option<T>`, which allows filtering the `Option` by a predicate.
19+
* Add overload for `Option<T>.SelectMany` that takes only a selector.
20+

0 commit comments

Comments
 (0)