fx-sharp  0.1
A collection of functional extensions for C#.
 All Classes Namespaces Files Functions Variables Enumerator
Maybe.cs
Go to the documentation of this file.
1 using FxSharp.Extensions;
2 using JetBrains.Annotations;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 
7 namespace FxSharp
8 {
9  /// <summary>
10  /// Static helper methods for Maybes.
11  /// </summary>
12  public static class Maybe
13  {
14  /// <summary>
15  /// Wrap a val type in a Maybe instance. The appropriate instance is selected
16  /// by distinguishing between a default val (for Nothing) and any other val
17  /// (for Just).
18  /// </summary>
19  /// <typeparam name="T">The wrapped type.</typeparam>
20  /// <param name="val">The wrapped object.</param>
21  /// <returns>A Maybe instance wrapping the given type.</returns>
22  public static Maybe<T> ToMaybe<T>(this T val)
23  {
24  // Value types are always valid unless explicitly set to Nothing.
25  if (typeof (T).IsValueType)
26  {
27  return Just(val);
28  }
29 
30  // At this point, T must be a reference type.
31  // ReSharper disable once CompareNonConstrainedGenericWithNull
32  return val != null
33  ? Just(val)
34  : Nothing<T>();
35  }
36 
37  /// <summary>
38  /// Convert a Maybe into an enumerable.
39  /// </summary>
40  /// <typeparam name="T"></typeparam>
41  /// <param name="maybe"></param>
42  /// <returns></returns>
43  public static IEnumerable<T> ToEnumerable<T>(this Maybe<T> maybe)
44  {
45  return maybe.Match(
46  just: t => t.ToEnumerable(),
47  nothing: Enumerable.Empty<T>);
48  }
49 
50  /// <summary>
51  /// </summary>
52  /// <typeparam name="T"></typeparam>
53  /// <param name="maybe"></param>
54  /// <returns></returns>
55  public static bool IsJust<T>(this Maybe<T> maybe)
56  {
57  return maybe.Match(
58  just: _ => true,
59  nothing: () => false);
60  }
61 
62  /// <summary>
63  /// </summary>
64  /// <typeparam name="T"></typeparam>
65  /// <param name="maybe"></param>
66  /// <returns></returns>
67  public static bool IsNothing<T>(this Maybe<T> maybe)
68  {
69  return maybe.Match(
70  just: _ => false,
71  nothing: () => true);
72  }
73 
74  /// <summary>
75  /// Create a Just Maybe instance by wrapping an arbitrary val.
76  /// </summary>
77  /// <typeparam name="T">The wrapped type.</typeparam>
78  /// <param name="val">The wrapped object.</param>
79  /// <returns>A Maybe instance wrapping the given type.</returns>
80  public static Maybe<T> Just<T>(T val)
81  {
82  return new Maybe<T>(val);
83  }
84 
85  /// <summary>
86  /// Create a Nothing Maybe instance.
87  /// </summary>
88  /// <typeparam name="T">The type of the absent val to represent.</typeparam>
89  /// <returns>A Maybe instance wrapping the given type.</returns>
90  public static Maybe<T> Nothing<T>()
91  {
92  return new Maybe<T>();
93  }
94  }
95 
96  /// <summary>
97  /// A type to describe a possibly absent val.
98  /// </summary>
99  /// <typeparam name="T">The type of the wrapped val.</typeparam>
100  public struct Maybe<T>
101  {
102  /// <summary>
103  /// Do we actually have a val? This is used instead of null to allow wrapping
104  /// val types as well.
105  /// </summary>
106  private readonly bool _hasValue;
107 
108  /// <summary>
109  /// The possibly present val.
110  /// </summary>
111  private readonly T _val;
112 
113  /// <summary>
114  /// Ctor for Just. The Nothing case is covered by the default ctor.
115  /// </summary>
116  /// <param name="val">The val to wrap.</param>
117  internal Maybe(T val)
118  {
119  _val = val;
120  _hasValue = true;
121  }
122 
123  /// <summary>
124  /// Get the stored val or the given default instead.
125  /// </summary>
126  /// <param name="other">The default val to return in case no val is stored.</param>
127  /// <returns>Stored or given default val.</returns>
128  /// <remarks>
129  /// <code>
130  /// var name = TelephoneDirectory
131  /// .LookUpName("+49394965006") // LookUpName returns Maybe[string]
132  /// .GetOrElse("John Doe");
133  /// </code>
134  /// </remarks>
135  [Pure]
136  public T GetOrElse(T other)
137  {
138  return _hasValue ? _val : other;
139  }
140 
141  /// <summary>
142  /// Map the function fn over the wrapped val if present. Wrap the result in as well.
143  /// </summary>
144  /// <typeparam name="TResult">The type of the result of fn.</typeparam>
145  /// <param name="fn">A function to apply the the wrapped val.</param>
146  /// <returns>The wrapped mapped val.</returns>
147  /// <remarks>
148  /// <code>
149  /// var name = TelephoneDirectory
150  /// .LookUpName("+49394965006") // LookUpName returns Maybe[string]
151  /// .Select(name => name.ToUpper());
152  /// </code>
153  /// </remarks>
154  [Pure]
155  public Maybe<TResult> Select<TResult>([NotNull] Func<T, TResult> fn)
156  {
157  return _hasValue
158  ? new Maybe<TResult>(fn(_val))
159  : new Maybe<TResult>();
160  }
161 
162  /// <summary>
163  /// Map the function fn over the wrapped val if present; discard the result.
164  /// </summary>
165  /// <param name="fn">A function to apply to the wrapped val.</param>
166  /// <returns>This.</returns>
167  /// <remarks>
168  /// <code>
169  /// TelephoneDirectory
170  /// .LookUpName("+49394965006") // LookUpName returns Maybe[string]
171  /// .Select_(name => view.DisplayName(name.ToUpper()));
172  /// </code>
173  /// </remarks>
174  [Pure]
175  public Maybe<T> Select_([NotNull] Action<T> fn)
176  {
177  if (_hasValue)
178  {
179  fn(_val);
180  }
181 
182  return this;
183  }
184 
185  /// <summary>
186  /// Map the function fn over the wrapped val if present.
187  /// </summary>
188  /// <typeparam name="TResult">The type of the result of fn.</typeparam>
189  /// <param name="fn">
190  /// A function to apply the the wrapped val which wraps the result in a Maybe.
191  /// </param>
192  /// <returns>The result of fn.</returns>
193  [Pure]
194  public Maybe<TResult> SelectMany<TResult>([NotNull] Func<T, Maybe<TResult>> fn)
195  {
196  return _hasValue ? fn(_val) : new Maybe<TResult>();
197  }
198 
199  /// <summary>
200  /// Map the first function over the wrapped value and the second function over the
201  /// result. If the wrapped value isn't present or the first or second functions return
202  /// no value, Nothing is returned. This variant of SelectMany enables LINQ's
203  /// syntactic sugar.
204  /// </summary>
205  /// <typeparam name="TInter">The result tpye of the first function.</typeparam>
206  /// <typeparam name="TResult">The result type of the second function.</typeparam>
207  /// <param name="firstFn">The first function to apply.</param>
208  /// <param name="secondFn">The second function to apply.</param>
209  /// <returns>The result of the functions combined.</returns>
210  /// <remarks>
211  /// <code>
212  /// var caller = from name in TelephoneDirectory.LookUpName("+49394965006") // LookUpName returns Maybe[string]
213  /// from photo in SocialNetwork.LookUpPhoto(name) // LookUpPhoto returns Maybe[Image]
214  /// select new Caller(name, photo);
215  /// </code>
216  /// </remarks>
217  [Pure]
218  public Maybe<TResult> SelectMany<TInter, TResult>(
219  [NotNull] Func<T, Maybe<TInter>> firstFn,
220  [NotNull] Func<T, TInter, TResult> secondFn)
221  {
222  return SelectMany(x => firstFn(x).SelectMany(y => secondFn(x, y).ToMaybe()));
223  }
224 
225  /// <summary>
226  /// Call the given function, if no val is present.
227  /// </summary>
228  /// <param name="fn">A function to call if no val is present.</param>
229  /// <returns>This instance.</returns>
230  /// <remarks>
231  /// <code>
232  /// TelephoneDirectory
233  /// .LookUpName("+49394965006") // LookUpName returns Maybe[string]
234  /// .Otherwise_(() => view.DisplayName("Unknown caller"));
235  /// </code>
236  /// </remarks>
237  [Pure]
238  public Maybe<T> Otherwise_([NotNull] Action fn)
239  {
240  if (!_hasValue)
241  {
242  fn();
243  }
244 
245  return this;
246  }
247 
248  /// <summary>
249  /// Map either the function just over the present val or call nothing.
250  /// </summary>
251  /// <typeparam name="TResult">The result type of nothing and just.</typeparam>
252  /// <param name="nothing">The function to call if no val is present.</param>
253  /// <param name="just">The function to call with a present val.</param>
254  /// <returns>The result of the either nothing or just functions.</returns>
255  /// <remarks>
256  /// <code>
257  /// var name = TelephoneDirectory
258  /// .LookUpName("+49394965006") // LookUpName returns Maybe[string]
259  /// .Match(
260  /// just: name => name.ToUpper(),
261  /// nothing: () => "John Doe");
262  /// </code>
263  /// </remarks>
264  [Pure]
265  public TResult Match<TResult>(
266  [NotNull] Func<TResult> nothing,
267  [NotNull] Func<T, TResult> just)
268  {
269  return _hasValue ? just(_val) : nothing();
270  }
271 
272  /// <summary>
273  /// Map either the function just over the present val or call nothing. Ignore the result.
274  /// </summary>
275  /// <param name="nothing">The function to call if no val is present.</param>
276  /// <param name="just">The function to call with a present val.</param>
277  /// <remarks>
278  /// <code>
279  /// TelephoneDirectory
280  /// .LookUpName("+49394965006") // LookUpName returns Maybe[string]
281  /// .Match_(
282  /// just: name => view.DisplayName(name.ToUpper()),
283  /// nothing: () => view.DisplayName("Unknown caller"));
284  /// </code>
285  /// </remarks>
286  [Pure]
287  public Maybe<T> Match_([NotNull] Action nothing, [NotNull] Action<T> just)
288  {
289  if (_hasValue)
290  {
291  just(_val);
292  }
293  else
294  {
295  nothing();
296  }
297 
298  return this;
299  }
300 
301  /// <summary>
302  /// </summary>
303  /// <returns></returns>
304  [Pure]
305  public override string ToString()
306  {
307  return _hasValue ? string.Format("Just {0}", _val) : "Nothing";
308  }
309  }
310 }
Maybe< T > Match_([NotNull] Action nothing, [NotNull] Action< T > just)
Map either the function just over the present val or call nothing. Ignore the result.
Definition: Maybe.cs:287
A type to describe a possibly absent val.
Definition: Maybe.cs:100
T GetOrElse(T other)
Get the stored val or the given default instead.
Definition: Maybe.cs:136
Maybe< T > Otherwise_([NotNull] Action fn)
Call the given function, if no val is present.
Definition: Maybe.cs:238
Maybe< T > Select_([NotNull] Action< T > fn)
Map the function fn over the wrapped val if present; discard the result.
Definition: Maybe.cs:175
override string ToString()
Definition: Maybe.cs:305