Skip to content

Commit aa474e3

Browse files
committed
readme
1 parent 22544d6 commit aa474e3

File tree

1 file changed

+120
-19
lines changed

1 file changed

+120
-19
lines changed

README.md

+120-19
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
Danom is a C# library that provides monadic structures to simplify functional programming patterns in C#, that enforces exhaustive matching by preventing direct value access (this is good).
66

77
## Key Features
8-
- Implementation of common monads like `Option`, `Result`, and `ResultOption`.
8+
- Implementation of common monads like [Option](#option), [Result](#result), and [ResultOption](#resultoption).
99
- Fluent API for chaining operations.
10-
- Error handling with monads.
10+
- [Error handling](#using-results) with monads.
1111
- Integration with async/await for asynchronous operations.
1212

1313
## Design Goals
@@ -16,9 +16,45 @@ Danom is a C# library that provides monadic structures to simplify functional pr
1616
- **Interoperability**: Seamless integration with existing C# code and libraries.
1717
- **Durability**: Prevent direct use of internal value, enforcing exhaustive matching.
1818

19-
## Options
19+
## Getting Started
2020

21-
Represents when an actual value might not exist for a value or named variable. An option has an underlying type and can hold a value of that type, or it might not have a value.
21+
Install the [Danom](https://www.nuget.org/packages/Danom/) NuGet package:
22+
23+
```
24+
PM> Install-Package Danom
25+
```
26+
27+
Or using the dotnet CLI
28+
```cmd
29+
dotnet add package Danom
30+
```
31+
32+
### Quick Start
33+
34+
```csharp
35+
using Danom;
36+
37+
// Create an Option
38+
var option = Option<int>.Some(5);
39+
40+
option.Match(
41+
some: x => Console.WriteLine("Value: {0}", x),
42+
none: () => Console.WriteLine("No value"));
43+
44+
// Create a Result
45+
public Result<int, string> TryDivide(int numerator, int denominator) =>
46+
denominator == 0
47+
? Result<int, string>.Error("Cannot divide by zero")
48+
: Result<int, string>.Ok(numerator / denominator);
49+
50+
TryDivide(10, 2)
51+
.Match(
52+
ok: x => Console.WriteLine("Result: {0}", x),
53+
error: e => Console.WriteLine("Error: {0}", e));
54+
55+
## Option
56+
57+
Represents when an actual value might not exist for a value or named variable. An option has an underlying type and can hold a value of that type, or it might not have a value. Options are a fantastic means of reducing primitive congestion in your code, and they are a much safer way to handle null values and virutally eliminate null reference exceptions.
2258

2359
### Creating Options
2460

@@ -28,7 +64,7 @@ var option = Option<int>.Some(5);
2864
var optionNone = Option<int>.None();
2965
```
3066

31-
### Using Options
67+
### Using Option
3268

3369
Options are commonly used when a operation might not return a value.
3470

@@ -49,46 +85,111 @@ TryFind(nums, x => x == 1)
4985
none: () => Console.WriteLine("Did not find number"));
5086

5187
// Mapping the value
52-
var optionSum =
88+
Option<int> optionSum =
5389
TryFind(nums, x => x == 1)
5490
.Map(x => x + 1);
5591

5692
// Binding the option
57-
var optionBindSum =
93+
Option<int> optionBindSum =
5894
TryFind(nums, x => x == 1)
5995
.Bind(num1 =>
6096
TryFind(nums, x => x == 2)
6197
.Map(num2 => num1 + num2));
6298

6399
// Handling "None"
64-
var optionDefault =
100+
Option<int> optionDefault =
65101
TryFind(nums, x => x == 4)
66102
.DefaultValue(99);
67103

68-
var optionDefaultWith =
104+
Option<int> optionDefaultWith =
69105
TryFind(nums, x => x == 4)
70106
.DefaultWith(() => 99); // useful if creating the value is expensive
71107
72-
var optionOrElse =
108+
Option<int> optionOrElse =
73109
TryFind(nums, x => x == 4)
74-
.OrElse(Option.Some(99));
110+
.OrElse(Option<int>.Some(99));
75111

76-
var optionOrElseWith =
112+
Option<int> optionOrElseWith =
77113
TryFind(nums, x => x == 4)
78-
.OrElseWith(() => 99);
114+
.OrElseWith(() => Option<int>.Some(99)); // useful if creating the value is expensive
79115
```
80116

81-
## Results
117+
## Result
118+
119+
Represents the result of an operation that can either succeed or fail. These results can be chained together allowing you to form error-tolerant pipelines. This lets you break up functionality like this into small pieces which are as composable as you need them to be. Also benefiting from the exhaustive matching.
120+
121+
### Creating Results
122+
123+
```csharp
124+
var result = Result<int, string>.Ok(5);
125+
// or, with an error
126+
var resultError = Result<int, string>.Error("An error occurred");
127+
// or, using the built-in Error type
128+
var resultErrors = Result<int>.Ok(5);
129+
var resultErrorsError = Result<int>.Error("An error occurred");
130+
var resultErrorsMultiError = Result<int>.Error(["An error occurred", "Another error occurred"]);
131+
var resultErrorsTyped = Result<int>.Error(new ResultErrors("error-key", "An error occurred"));
132+
```
82133

83-
Represents the result of an operation that can either succeed or fail. These
84-
results can be chained together to form pipelines of error handling.
134+
### Using Results
85135

136+
Results are commonly used when an operation might not succeed, and you want to manage the _expected_ errors.
86137

87-
--
138+
```csharp
139+
public Result<int, string> TryDivide(int numerator, int denominator) =>
140+
denominator == 0
141+
? Result<int, string>.Error("Cannot divide by zero")
142+
: Result<int, string>.Ok(numerator / denominator);
143+
144+
// Exhasutive matching
145+
TryDivide(10, 2)
146+
.Match(
147+
ok: x => Console.WriteLine("Result: {0}", x),
148+
error: e => Console.WriteLine("Error: {0}", e));
149+
150+
// Mapping the value
151+
Result<int, string> resultSum =
152+
TryDivide(10, 2)
153+
.Map(x => x + 1);
154+
155+
// Binding the result (i.e., when a nested operation also returns a Result)
156+
Result<int, string> resultBindSum =
157+
TryDivide(10, 2)
158+
.Bind(num1 =>
159+
TryDivide(20, 2)
160+
.Map(num2 =>
161+
num1 + num2));
162+
163+
// Handling errors
164+
Result<int, string> resultDefault =
165+
TryDivide(10, 0)
166+
.DefaultValue(99);
167+
168+
Result<int, string> resultDefaultWith =
169+
TryDivide(10, 0)
170+
.DefaultWith(() => 99); // useful if creating the value is expensive
171+
172+
Result<int, string> resultOrElse =
173+
TryDivide(10, 0)
174+
.OrElse(Result<int, string>.Ok(99));
175+
176+
Result<int, string> resultOrElseWith =
177+
TryDivide(10, 0)
178+
.OrElseWith(() =>
179+
Result<int, string>.Ok(99)); // useful if creating the value is expensive
180+
```
181+
182+
Since error messages are frequently represented as string collections, often with keys (e.g., for validation), the `ResultErrors` type is provided to simplify Result creation. The flexible constructor allows errors to be initialized with a single string, a collection of strings, or a key-value pair.
183+
184+
```csharp
185+
Result<int, ResultErrors> resultErrors = Result<int>.Ok(5);
186+
Result<int, ResultErrors> resultErrorsError = Result<int>.Error("An error occurred");
187+
Result<int, ResultErrors> resultErrorsMultiError = Result<int>.Error(["An error occurred", "Another error occurred"]);
188+
Result<int, ResultErrors> resultErrorsTyped = Result<int>.Error(new ResultErrors("error-key", "An error occurred"));
189+
```
88190

89-
## ResultOptions
191+
## ResultOption
90192

91-
--
92193

93194
## Contribute
94195

0 commit comments

Comments
 (0)