-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathTryGeneral.cs
100 lines (84 loc) · 5.13 KB
/
TryGeneral.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
using System;
using System.Collections.Generic;
using System.Linq;
namespace FuncSharp.Examples;
public class TryGeneral
{
private void TransformingSuccessWithMap()
{
Try<int, NetworkOperationError> number = Api.GetNumber();
Try<decimal, NetworkOperationError> castedNumber = number.Map(r => (decimal)r);
Try<string, NetworkOperationError> stringifiedNumber = number.Map(r => r.ToString());
}
private void TransformingErrorWithMapError()
{
Try<int, NetworkOperationError> number = Api.GetNumber();
// You can use MapError to map for example from exception to another type you use to represent errors. Or map to logging messages.
Try<int, string> flooredMultiplicationResult = number.MapError(e => e.ToString());
}
private void HandlingNestedTriesWithFlatMap()
{
Try<int, NetworkOperationError> number = Api.GetNumber();
Try<Try<int, NetworkOperationError>, NetworkOperationError> doubleNumber = number.Map(r => Api.DoubleNumber(r));
// This try succeeds only if both tries succeed. However the second lambda is only executed in case the first try is successful.
Try<int, NetworkOperationError> doubleNumberFlattened = number.FlatMap(r => Api.DoubleNumber(r));
}
private void RetrievingValues()
{
// A try is a specific case of coproduct. Match methods are applicable for all coproduct types.
Try<int, NetworkOperationError> number = Api.GetNumber();
// Match is the preferred way how to retrieve values of tries (and coproducts in general). It is typesafe and future proof.
// This overload takes two functions. Each of those have to return a value and result is stored in the stringifiedNumber variable.
string stringifiedNumber = number.Match(
result => result.ToString(),
_ => "Unfortunately, we failed to obtain a number from the server."
);
// This overload accepts two optional functions. If either of them isn't provided, nothing happens for that case.
number.Match(
n => Console.Write($"Operation successful, result is: {n}."),
_ => Console.Write("Operation failed, try again.")
);
number.Match(n => Console.Write($"Operation successful, result is: {n}."));
number.Match(ifError: _ => Console.Write("Operation failed, try again."));
// Get method will throw an exception for unsuccessful tries that have exception as the error. Using it is an anti-pattern.
// You should rather use Match to branch your code into individual cases where each case is guaranteed to work.
// This might be needed on the boundary with some other framework where you have to work with exceptions.
Try<int, InvalidOperationException> numberWithException = number.MapError(e => new InvalidOperationException());
int numberValue1 = numberWithException.Get();
// You can also configure the exception that is thrown by mapping the error inside Get directly.
int numberValue2 = number.Get(e => new InvalidOperationException());
int numberValue3 = numberWithException.Get(ex => new Exception("Error when retrieving number", innerException: ex));
// Because try is a coproduct, you can check the value directly. On try, there are named properties for this.
Option<int> successResult1 = number.Success;
Option<NetworkOperationError> errorResult = number.Error;
}
private void AggregatingMultipleTriesIntoSingleResult()
{
// You can combine independent tries into a single value or a list of errors in case any of the tries is erroneous.
Try<int, NetworkOperationError> number1 = Api.GetNumber();
Try<int, NetworkOperationError> number2 = Api.GetNumber();
Try<int, NetworkOperationError> number3 = Api.GetNumber();
Try<int, IReadOnlyList<NetworkOperationError>> sumOfThreeNumbers = Try.Aggregate(
t1: number1,
t2: number2,
t3: number3,
success: (n1, n2, n3) => n1 + n2 + n3
);
// Great examples of aggregating tries can also be found when parsing. See what the Person.Parse method does.
Try<Person, IReadOnlyList<PersonParsingError>> mom = Person.Parse("Jane Doe", "24", "185");
Try<Person, IReadOnlyList<PersonParsingError>> dad = Person.Parse("John Doe", "29", "185");
Try<Person, IReadOnlyList<PersonParsingError>> son = Person.Parse("Jimmy Doe", "1", "75");
Try<(Person Dad, Person Mom, Person Son), IReadOnlyList<PersonParsingError>> family = Try.Aggregate(
t1: mom,
t2: dad,
t3: son,
success: (m, d, s) => (Dad: d, Mom: m, Son: s)
);
}
private void AggregatingCollectionOfTries(int numberCount)
{
IEnumerable<Try<int, NetworkOperationError>> numbers = Enumerable.Range(0, numberCount).Select(_ => Api.GetNumber());
// Contains all the numbers if their retrieval succeeded. Or all the errors from the ones that failed. Success results are lost in such case.
Try<IReadOnlyList<int>, IReadOnlyList<NetworkOperationError>> combinedResult = Try.Aggregate(numbers);
}
}