BadScript 2
Loading...
Searching...
No Matches
ErrorHandling.cs
Go to the documentation of this file.
1//Use project level define(s) when referencing with Paket.
2//#define ERRH_INTERNAL // Uncomment or define at build time to set accessibility to internal.
3//#define ERRH_ENABLE_INLINE_METHODS // Uncomment or define at build time to enable method inlining when compiling for >= NET 4.5.
4//#define ERRH_ADD_MAYBE_METHODS // Uncomment or define at build time to add methods that use Maybe type
5
6using System;
7using System.Collections.Generic;
8using System.Linq;
9#if ERRH_ADD_MAYBE_METHODS
10using CSharpx;
11#endif
12
14{
15#if !ERRH_INTERNAL
16 public
17#endif
18 internal enum ResultType
19 {
20 Ok,
21 Bad,
22 }
23
29#if !ERRH_INTERNAL
30 public
31#endif
32 internal abstract class Result<TSuccess, TMessage>
33 {
34 protected Result(ResultType tag)
35 {
36 Tag = tag;
37 }
38
39 public ResultType Tag { get; }
40
41 public override string ToString()
42 {
43 switch (Tag)
44 {
45 default:
47
48 return string.Format("OK: {0} - {1}",
49 ok.Success,
50 string.Join(Environment.NewLine, ok.Messages.Select(v => v.ToString()))
51 );
52 case ResultType.Bad:
54
55 return string.Format("Error: {0}",
56 string.Join(Environment.NewLine, bad.Messages.Select(v => v.ToString()))
57 );
58 }
59 }
60 }
61
67#if !ERRH_INTERNAL
68 public
69#endif
70 internal sealed class Ok<TSuccess, TMessage> : Result<TSuccess, TMessage>
71 {
72 private readonly Tuple<TSuccess, IEnumerable<TMessage>> _value;
73
74 public Ok(TSuccess success, IEnumerable<TMessage> messages)
75 : base(ResultType.Ok)
76 {
77 if (messages == null)
78 {
79 throw new ArgumentNullException(nameof(messages));
80 }
81
82 _value = Tuple.Create(success, messages);
83 }
84
85 public TSuccess Success => _value.Item1;
86
87 public IEnumerable<TMessage> Messages => _value.Item2;
88 }
89
95#if !ERRH_INTERNAL
96 public
97#endif
98 internal sealed class Bad<TSuccess, TMessage> : Result<TSuccess, TMessage>
99 {
100 public Bad(IEnumerable<TMessage> messages)
101 : base(ResultType.Bad)
102 {
103 if (messages == null)
104 {
105 throw new ArgumentException(nameof(messages));
106 }
107
108 Messages = messages;
109 }
110
111 public IEnumerable<TMessage> Messages { get; }
112 }
113
114#if !ERRH_INTERNAL
115 public
116#endif
117 internal static class Result
118 {
122 public static Result<TSuccess, TMessage> FailWith<TSuccess, TMessage>(IEnumerable<TMessage> messages)
123 {
124 if (messages == null)
125 {
126 throw new ArgumentException(nameof(messages));
127 }
128
129 return new Bad<TSuccess, TMessage>(messages);
130 }
131
136 {
137 if (message == null)
138 {
139 throw new ArgumentException(nameof(message));
140 }
141
142 return new Bad<TSuccess, TMessage>(new[] { message });
143 }
144
149 {
150 return new Ok<TSuccess, TMessage>(value, Enumerable.Empty<TMessage>());
151 }
152
156 public static Result<TSuccess, TMessage> Succeed<TSuccess, TMessage>(TSuccess value, TMessage message)
157 {
158 if (message == null)
159 {
160 throw new ArgumentException(nameof(message));
161 }
162
163 return new Ok<TSuccess, TMessage>(value,
164 new[] { message }
165 );
166 }
167
172 TSuccess value,
173 IEnumerable<TMessage> messages)
174 {
175 if (messages == null)
176 {
177 throw new ArgumentException(nameof(messages));
178 }
179
180 return new Ok<TSuccess, TMessage>(value, messages);
181 }
182
186 public static Result<TSuccess, Exception> Try<TSuccess>(Func<TSuccess> func)
187 {
188 if (func == null)
189 {
190 throw new ArgumentException(nameof(func));
191 }
192
193 try
194 {
195 return new Ok<TSuccess, Exception>(func(),
196 Enumerable.Empty<Exception>()
197 );
198 }
199 catch (Exception ex)
200 {
201 return new Bad<TSuccess, Exception>(new[] { ex });
202 }
203 }
204 }
205
206#if !ERRH_INTERNAL
207 public
208#endif
209 internal static class Trial
210 {
214#if ERRH_ENABLE_INLINE_METHODS
215 [MethodImpl(MethodImplOptions.AggressiveInlining)]
216#endif
218 {
219 return new Ok<TSuccess, TMessage>(value, Enumerable.Empty<TMessage>());
220 }
221
225#if ERRH_ENABLE_INLINE_METHODS
226 [MethodImpl(MethodImplOptions.AggressiveInlining)]
227#endif
229 {
230 return new Ok<TSuccess, TMessage>(value, Enumerable.Empty<TMessage>());
231 }
232
236#if ERRH_ENABLE_INLINE_METHODS
237 [MethodImpl(MethodImplOptions.AggressiveInlining)]
238#endif
239 public static Result<TSuccess, TMessage> Warn<TSuccess, TMessage>(TMessage message, TSuccess value)
240 {
241 if (message == null)
242 {
243 throw new ArgumentException(nameof(message));
244 }
245
246 return new Ok<TSuccess, TMessage>(value,
247 new[] { message }
248 );
249 }
250
254#if ERRH_ENABLE_INLINE_METHODS
255 [MethodImpl(MethodImplOptions.AggressiveInlining)]
256#endif
258 {
259 if (message == null)
260 {
261 throw new ArgumentException(nameof(message));
262 }
263
264 return new Bad<TSuccess, TMessage>(new[] { message });
265 }
266
270#if ERRH_ENABLE_INLINE_METHODS
271 [MethodImpl(MethodImplOptions.AggressiveInlining)]
272#endif
274 {
275 return result.Tag == ResultType.Bad;
276 }
277
281#if ERRH_ENABLE_INLINE_METHODS
282 [MethodImpl(MethodImplOptions.AggressiveInlining)]
283#endif
285 Func<TSuccess, IEnumerable<TMessage>, TResult> successFunc,
286 Func<IEnumerable<TMessage>, TResult> failureFunc,
287 Result<TSuccess, TMessage> trialResult)
288 {
289 if (successFunc == null)
290 {
291 throw new ArgumentException(nameof(successFunc));
292 }
293
294 if (failureFunc == null)
295 {
296 throw new ArgumentException(nameof(failureFunc));
297 }
298
300
301 if (ok != null)
302 {
303 return successFunc(ok.Success, ok.Messages);
304 }
305
307
308 return failureFunc(bad.Messages);
309 }
310
315#if ERRH_ENABLE_INLINE_METHODS
316 [MethodImpl(MethodImplOptions.AggressiveInlining)]
317#endif
319 {
320 Func<IEnumerable<TMessage>, TSuccess> raiseExn = msgs =>
321 {
322 throw new Exception(string.Join(Environment.NewLine,
323 msgs.Select(m => m.ToString())
324 )
325 );
326 };
327
328 return Either((succ, _) => succ, raiseExn, result);
329 }
330
334#if ERRH_ENABLE_INLINE_METHODS
335 [MethodImpl(MethodImplOptions.AggressiveInlining)]
336#endif
337 public static Result<TSuccess, TMessage> MergeMessages<TSuccess, TMessage>(IEnumerable<TMessage> messages,
339 {
340 if (messages == null)
341 {
342 throw new ArgumentException(nameof(messages));
343 }
344
345 Func<TSuccess, IEnumerable<TMessage>, Result<TSuccess, TMessage>> successFunc =
346 (succ, msgs) =>
347 new Ok<TSuccess, TMessage>(succ,
348 messages.Concat(msgs)
349 );
350
351 Func<IEnumerable<TMessage>, Result<TSuccess, TMessage>> failureFunc =
352 errors => new Bad<TSuccess, TMessage>(errors.Concat(messages));
353
354 return Either(successFunc, failureFunc, result);
355 }
356
361#if ERRH_ENABLE_INLINE_METHODS
362 [MethodImpl(MethodImplOptions.AggressiveInlining)]
363#endif
365 Func<TValue, Result<TSuccess, TMessage>> func,
367 {
368 if (func == null)
369 {
370 throw new ArgumentException(nameof(func));
371 }
372
373 Func<TValue, IEnumerable<TMessage>, Result<TSuccess, TMessage>> successFunc =
374 (succ, msgs) => MergeMessages(msgs, func(succ));
375
376 Func<IEnumerable<TMessage>, Result<TSuccess, TMessage>> failureFunc =
377 messages => new Bad<TSuccess, TMessage>(messages);
378
379 return Either(successFunc, failureFunc, result);
380 }
381
385#if ERRH_ENABLE_INLINE_METHODS
386 [MethodImpl(MethodImplOptions.AggressiveInlining)]
387#endif
389 Result<Result<TSuccess, TMessage>, TMessage> result)
390 {
391 return Bind(x => x, result);
392 }
393
398#if ERRH_ENABLE_INLINE_METHODS
399 [MethodImpl(MethodImplOptions.AggressiveInlining)]
400#endif
402 Result<Func<TValue, TSuccess>, TMessage> wrappedFunction,
404 {
405 if (wrappedFunction == null)
406 {
407 throw new ArgumentException(nameof(wrappedFunction));
408 }
409
410 if (wrappedFunction.Tag == ResultType.Ok && result.Tag == ResultType.Ok)
411 {
412 Ok<Func<TValue, TSuccess>, TMessage> ok1 = (Ok<Func<TValue, TSuccess>, TMessage>)wrappedFunction;
414
415 return new Ok<TSuccess, TMessage>(ok1.Success(ok2.Success),
416 ok1.Messages.Concat(ok2.Messages)
417 );
418 }
419
420 if (wrappedFunction.Tag == ResultType.Bad && result.Tag == ResultType.Ok)
421 {
422 return new Bad<TSuccess, TMessage>(((Bad<TValue, TMessage>)result).Messages);
423 }
424
425 if (wrappedFunction.Tag == ResultType.Ok && result.Tag == ResultType.Bad)
426 {
427 return new Bad<TSuccess, TMessage>(((Bad<TValue, TMessage>)result).Messages);
428 }
429
430 Bad<Func<TValue, TSuccess>, TMessage> bad1 = (Bad<Func<TValue, TSuccess>, TMessage>)wrappedFunction;
432
433 return new Bad<TSuccess, TMessage>(bad1.Messages.Concat(bad2.Messages));
434 }
435
439#if ERRH_ENABLE_INLINE_METHODS
440 [MethodImpl(MethodImplOptions.AggressiveInlining)]
441#endif
442 public static Result<TSuccess, TMessage> Lift<TValue, TSuccess, TMessage>(Func<TValue, TSuccess> func,
444 {
445 return Apply(Ok<Func<TValue, TSuccess>, TMessage>(func), result);
446 }
447
451#if ERRH_ENABLE_INLINE_METHODS
452 [MethodImpl(MethodImplOptions.AggressiveInlining)]
453#endif
455 Func<TSuccess, Func<TMessage, TSuccess1>> func,
458 {
459 return Apply(Lift(func, first), second);
460 }
461
466#if ERRH_ENABLE_INLINE_METHODS
467 [MethodImpl(MethodImplOptions.AggressiveInlining)]
468#endif
470 IEnumerable<Result<TSuccess, TMessage>> xs)
471 {
472 return Lift(Enumerable.Reverse,
473 xs.Aggregate<Result<TSuccess, TMessage>, Result<IEnumerable<TSuccess>, TMessage>,
474 Result<IEnumerable<TSuccess>, TMessage>>(null,
475 (result, next) =>
476 {
477 if (result.Tag == ResultType.Ok &&
478 next.Tag == ResultType.Ok)
479 {
480 Ok<IEnumerable<TSuccess>, TMessage> ok1 =
481 (Ok<IEnumerable<TSuccess>, TMessage>)
482 result;
483
484 Ok<TSuccess, TMessage> ok2 =
485 (Ok<TSuccess, TMessage>)next;
486
487 return
488 new Ok<IEnumerable<TSuccess>,
489 TMessage>(Enumerable
490 .Empty<TSuccess>()
491 .Concat(new[] { ok2.Success }
492 )
493 .Concat(ok1.Success),
494 ok1.Messages.Concat(ok2.Messages)
495 );
496 }
497
498 if ((result.Tag == ResultType.Ok &&
499 next.Tag == ResultType.Bad) ||
500 (result.Tag == ResultType.Bad &&
501 next.Tag == ResultType.Ok))
502 {
503 IEnumerable<TMessage> m1 =
504 result.Tag == ResultType.Ok
505 ? ((Ok<IEnumerable<TSuccess>,
506 TMessage>)result)
507 .Messages
508 : ((Bad<TSuccess, TMessage>)next)
509 .Messages;
510
511 IEnumerable<TMessage> m2 =
512 result.Tag == ResultType.Bad
513 ? ((Bad<IEnumerable<TSuccess>,
514 TMessage>)result)
515 .Messages
516 : ((Ok<TSuccess, TMessage>)next)
517 .Messages;
518
519 return new Bad<IEnumerable<TSuccess>,
520 TMessage>(m1.Concat(m2));
521 }
522
523 Bad<IEnumerable<TSuccess>, TMessage> bad1 =
524 (Bad<IEnumerable<TSuccess>, TMessage>)
525 result;
526
529
530 return new
531 Bad<IEnumerable<TSuccess>, TMessage>(bad1
532 .Messages.Concat(bad2.Messages)
533 );
534 },
535 x => x
536 )
537 );
538 }
539 }
540
544#if !ERRH_INTERNAL
545 public
546#endif
547 internal static class ResultExtensions
548 {
552#if ERRH_ENABLE_INLINE_METHODS
553 [MethodImpl(MethodImplOptions.AggressiveInlining)]
554#endif
555 public static void Match<TSuccess, TMessage>(this Result<TSuccess, TMessage> result,
556 Action<TSuccess, IEnumerable<TMessage>> ifSuccess,
557 Action<IEnumerable<TMessage>> ifFailure)
558 {
559 if (ifSuccess == null)
560 {
561 throw new ArgumentException(nameof(ifSuccess));
562 }
563
564 if (ifFailure == null)
565 {
566 throw new ArgumentException(nameof(ifFailure));
567 }
568
570
571 if (ok != null)
572 {
573 ifSuccess(ok.Success, ok.Messages);
574
575 return;
576 }
577
579 ifFailure(bad.Messages);
580 }
581
585#if ERRH_ENABLE_INLINE_METHODS
586 [MethodImpl(MethodImplOptions.AggressiveInlining)]
587#endif
589 Func<TSuccess, IEnumerable<TMessage>, TResult>
590 ifSuccess,
591 Func<IEnumerable<TMessage>, TResult> ifFailure)
592 {
593 return Trial.Either(ifSuccess, ifFailure, result);
594 }
595
599#if ERRH_ENABLE_INLINE_METHODS
600 [MethodImpl(MethodImplOptions.AggressiveInlining)]
601#endif
602 public static Result<TResult, TMessage> Map<TSuccess, TMessage, TResult>(this Result<TSuccess, TMessage> result,
603 Func<TSuccess, TResult> func)
604 {
605 return Trial.Lift(func, result);
606 }
607
612#if ERRH_ENABLE_INLINE_METHODS
613 [MethodImpl(MethodImplOptions.AggressiveInlining)]
614#endif
615 public static Result<IEnumerable<TSuccess>, TMessage> Collect<TSuccess, TMessage>(
616 this IEnumerable<Result<TSuccess, TMessage>> values)
617 {
618 return Trial.Collect(values);
619 }
620
625#if ERRH_ENABLE_INLINE_METHODS
626 [MethodImpl(MethodImplOptions.AggressiveInlining)]
627#endif
628 public static Result<IEnumerable<TSuccess>, TMessage> Flatten<TSuccess, TMessage>(
629 this Result<IEnumerable<Result<TSuccess, TMessage>>, TMessage> result)
630 {
631 if (result.Tag == ResultType.Ok)
632 {
635 IEnumerable<Result<TSuccess, TMessage>> values = ok.Success;
636 Result<IEnumerable<TSuccess>, TMessage> result1 = Collect(values);
637
638 if (result1.Tag == ResultType.Ok)
639 {
640 Ok<IEnumerable<TSuccess>, TMessage> ok1 = (Ok<IEnumerable<TSuccess>, TMessage>)result1;
641
642 return new Ok<IEnumerable<TSuccess>, TMessage>(ok1.Success, ok1.Messages);
643 }
644
645 Bad<IEnumerable<TSuccess>, TMessage> bad1 = (Bad<IEnumerable<TSuccess>, TMessage>)result1;
646
647 return new Bad<IEnumerable<TSuccess>, TMessage>(bad1.Messages);
648 }
649
652
653 return new Bad<IEnumerable<TSuccess>, TMessage>(bad.Messages);
654 }
655
660#if ERRH_ENABLE_INLINE_METHODS
661 [MethodImpl(MethodImplOptions.AggressiveInlining)]
662#endif
663 public static Result<TResult, TMessage> SelectMany<TSuccess, TMessage, TResult>(
664 this Result<TSuccess, TMessage> result,
665 Func<TSuccess, Result<TResult, TMessage>> func)
666 {
667 return Trial.Bind(func, result);
668 }
669
675#if ERRH_ENABLE_INLINE_METHODS
676 [MethodImpl(MethodImplOptions.AggressiveInlining)]
677#endif
678 public static Result<TResult, TMessage> SelectMany<TSuccess, TMessage, TValue, TResult>(
679 this Result<TSuccess, TMessage> result,
680 Func<TSuccess, Result<TValue, TMessage>> func,
681 Func<TSuccess, TValue, TResult> mapperFunc)
682 {
683 if (func == null)
684 {
685 throw new ArgumentException(nameof(func));
686 }
687
688 if (mapperFunc == null)
689 {
690 throw new ArgumentException(nameof(mapperFunc));
691 }
692
693 Func<TSuccess, Func<TValue, TResult>> curriedMapper = suc => val => mapperFunc(suc, val);
694
695 Func<
699 > liftedMapper = (a, b) => Trial.Lift2(curriedMapper, a, b);
700 Result<TValue, TMessage> v = Trial.Bind(func, result);
701
702 return liftedMapper(result, v);
703 }
704
708#if ERRH_ENABLE_INLINE_METHODS
709 [MethodImpl(MethodImplOptions.AggressiveInlining)]
710#endif
711 public static Result<TResult, TMessage> Select<TSuccess, TMessage, TResult>(
712 this Result<TSuccess, TMessage> result,
713 Func<TSuccess, TResult> func)
714 {
715 return Trial.Lift(func, result);
716 }
717
721#if ERRH_ENABLE_INLINE_METHODS
722 [MethodImpl(MethodImplOptions.AggressiveInlining)]
723#endif
724 public static IEnumerable<TMessage> FailedWith<TSuccess, TMessage>(this Result<TSuccess, TMessage> result)
725 {
726 if (result.Tag == ResultType.Ok)
727 {
729
730 throw new Exception(string.Format("Result was a success: {0} - {1}",
731 ok.Success,
732 string.Join(Environment.NewLine,
733 ok.Messages.Select(m => m.ToString())
734 )
735 )
736 );
737 }
738
740
741 return bad.Messages;
742 }
743
747#if ERRH_ENABLE_INLINE_METHODS
748 [MethodImpl(MethodImplOptions.AggressiveInlining)]
749#endif
750 public static TSuccess SucceededWith<TSuccess, TMessage>(this Result<TSuccess, TMessage> result)
751 {
752 if (result.Tag == ResultType.Ok)
753 {
755
756 return ok.Success;
757 }
758
760
761 throw new Exception(string.Format("Result was an error: {0}",
762 string.Join(Environment.NewLine, bad.Messages.Select(m => m.ToString()))
763 )
764 );
765 }
766
770 public static IEnumerable<TMessage> SuccessMessages<TSuccess, TMessage>(this Result<TSuccess, TMessage> result)
771 {
772 if (result.Tag == ResultType.Ok)
773 {
775
776 return ok.Messages;
777 }
778
779 return Enumerable.Empty<TMessage>();
780 }
781
782#if ERRH_ADD_MAYBE_METHODS
783#if ERRH_ENABLE_INLINE_METHODS
784 [MethodImpl(MethodImplOptions.AggressiveInlining)]
785#endif
789 public static Maybe<TSuccess> ToMaybe<TSuccess, TMessage>(this Result<TSuccess, TMessage> result)
790 {
791 if (result.Tag == ResultType.Ok)
792 {
794
795 return Maybe.Just(ok.Success);
796 }
797
798 return Maybe.Nothing<TSuccess>();
799 }
800#endif
801 }
802}
The Maybe type models an optional value. A value of type Maybe a either contains a value of type a (r...
Definition Maybe.cs:33
Represents the result of a failed computation.
Bad(IEnumerable< TMessage > messages)
IEnumerable< TMessage > Messages
Represents the result of a successful computation.
IEnumerable< TMessage > Messages
readonly Tuple< TSuccess, IEnumerable< TMessage > > _value
Ok(TSuccess success, IEnumerable< TMessage > messages)
Extensions methods for easier usage.
Represents the result of a computation.
static Result< TSuccess, TMessage > Succeed< TSuccess, TMessage >(TSuccess value)
Creates a Success result with the given value.
static Result< TSuccess, Exception > Try< TSuccess >(Func< TSuccess > func)
Executes the given function on a given success or captures the failure.
static Result< TSuccess, TMessage > FailWith< TSuccess, TMessage >(IEnumerable< TMessage > messages)
Creates a Failure result with the given messages.
static Result< TSuccess, TMessage > Ok< TSuccess, TMessage >(TSuccess value)
Wraps a value in a Success.
static Result< TSuccess, TMessage > Pass< TSuccess, TMessage >(TSuccess value)
Wraps a value in a Success.
static TResult Either< TSuccess, TMessage, TResult >(Func< TSuccess, IEnumerable< TMessage >, TResult > successFunc, Func< IEnumerable< TMessage >, TResult > failureFunc, Result< TSuccess, TMessage > trialResult)
Takes a Result and maps it with successFunc if it is a Success otherwise it maps it with failureFunc.
static Result< TSuccess, TMessage > Apply< TValue, TSuccess, TMessage >(Result< Func< TValue, TSuccess >, TMessage > wrappedFunction, Result< TValue, TMessage > result)
If the wrapped function is a success and the given result is a success the function is applied on the...
static Result< TSuccess, TMessage > Lift< TValue, TSuccess, TMessage >(Func< TValue, TSuccess > func, Result< TValue, TMessage > result)
Lifts a function into a Result container and applies it on the given result.
static bool Failed< TSuccess, TMessage >(Result< TSuccess, TMessage > result)
Returns true if the result was not successful.
static Result< TSuccess1, TMessage1 > Lift2< TSuccess, TMessage, TSuccess1, TMessage1 >(Func< TSuccess, Func< TMessage, TSuccess1 > > func, Result< TSuccess, TMessage1 > first, Result< TMessage, TMessage1 > second)
Promote a function to a monad/applicative, scanning the monadic/applicative arguments from left to ri...
static Result< TSuccess, TMessage > Flatten< TSuccess, TMessage >(Result< Result< TSuccess, TMessage >, TMessage > result)
Flattens a nested result given the Failure types are equal.
static Result< IEnumerable< TSuccess >, TMessage > Collect< TSuccess, TMessage >(IEnumerable< Result< TSuccess, TMessage > > xs)
Collects a sequence of Results and accumulates their values. If the sequence contains an error the er...
static TSuccess ReturnOrFail< TSuccess, TMessage >(Result< TSuccess, TMessage > result)
If the given result is a Success the wrapped value will be returned. Otherwise the function throws an...
static Result< TSuccess, TMessage > Warn< TSuccess, TMessage >(TMessage message, TSuccess value)
Wraps a value in a Success and adds a message.
static Result< TSuccess, TMessage > Bind< TValue, TSuccess, TMessage >(Func< TValue, Result< TSuccess, TMessage > > func, Result< TValue, TMessage > result)
If the result is a Success it executes the given function on the value. Otherwise the exisiting failu...
static Result< TSuccess, TMessage > Fail< TSuccess, TMessage >(TMessage message)
Wraps a message in a Failure.
static Result< TSuccess, TMessage > MergeMessages< TSuccess, TMessage >(IEnumerable< TMessage > messages, Result< TSuccess, TMessage > result)
Appends the given messages with the messages in the given result.