-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7218a17
commit 7b66a32
Showing
13 changed files
with
228 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
This article contains a variety of code samples demonstrating common usages for various parts of the Rascal library. All the samples source code can be found [here](https://github.com/thinker227/Rascal/tree/main/samples). | ||
|
||
<br/> | ||
|
||
## Creating results | ||
|
||
Results in Rascal can be created in a variety of ways, the two most common of which are through the [`Ok`](/api/Rascal.Prelude.html#Rascal_Prelude_Ok__1___0_) and [`Err`](/api/Rascal.Prelude.html#Rascal_Prelude_Err__1_Rascal_Error_) methods defined in the prelude, or through implicitly converting ok values or errors into results. | ||
|
||
[!code-csharp[](../../samples/Construction.csx#L7-L13)] | ||
|
||
<br/> | ||
|
||
## Mapping | ||
|
||
"Mapping" refers to taking a result containing some value some type (`T`) and *mapping* said value to a new value of some other type (`TNew`). The principal method of mapping is the aptly named [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__). | ||
|
||
[!code-csharp[](../../samples/Map.csx#L6-L14)] | ||
|
||
<br/> | ||
|
||
Another operation, commonly referred to as "bind" or "chaining", exists, which looks quite similar to mapping, the only difference being that the lambda you supply to the method returns a *new* result rather than a plain value. The principal method of chaining is [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___), which can be read as "a, then b, then c". | ||
|
||
[!code-csharp[](../../samples/Then.csx#L6-L19)] | ||
|
||
<br/> | ||
|
||
[`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) and [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) together make up the core of the [`Result<T>`](/api/Rascal.Result-1.html) type, allowing for chaining multiple operations on a single result. In functional terms, these are what makes [`Result<T>`](/api/Rascal.Result-1.html) a functor and monad respectively (although not an applicative). | ||
|
||
<br/> | ||
|
||
> [!TIP] | ||
> The aliases [`Select`](/api/Rascal.Result-1.html#Rascal_Result_1_Select__1_System_Func__0___0__) and [`SelectMany`](/api/Rascal.Result-1.html#Rascal_Result_1_SelectMany__1_System_Func__0_Rascal_Result___0___) are available for [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) and [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) respectively. These exist to supply support for *query expressions* as an alternative to method chaining. Query syntax can in specific situations be more readable than the method chaining alternative, although in *most* scenarios, method chaning is better. [`Select`](/api/Rascal.Result-1.html#Rascal_Result_1_Select__1_System_Func__0___0__) and [`SelectMany`](/api/Rascal.Result-1.html#Rascal_Result_1_SelectMany__1_System_Func__0_Rascal_Result___0___) should ***not*** be used outside query syntax. | ||
<br/> | ||
|
||
### Combine | ||
|
||
[`Combine`](/api/Rascal.Result-1.html#Rascal_Result_1_Combine__1_Rascal_Result___0__) is an addition to [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) and [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) which streamlines the specific case where you have two results and want to *combine* them into a single result only if both results are ok. | ||
|
||
[!code-csharp[](../../samples/Combine.csx#L6-L15)] | ||
|
||
<br/> | ||
|
||
## Validation | ||
|
||
Rascal supports a simple way of validating the value of a result, returning an error in case the validation fails. | ||
|
||
[!code-csharp[](../../samples/Validation.csx#L8-L24)] | ||
|
||
<br/> | ||
|
||
## Exception handling | ||
|
||
One of the major kinks of adapting C# into a more functional style (such as using results) is the already existing standard of using exceptions for error-handling. Exceptions have *many* flaws, and result types explicitly exist to provide a better alternative to exceptions, but Rascal nontheless provides a way to interoperate with traditional exception-based error handling. | ||
|
||
The [`Try`](/api/Rascal.Prelude.html#Rascal_Prelude_Try__1_System_Func___0__) method in the prelude is the premiere exception-handling method, which runs another function inside a `try`-`catch` block, and returns an [`ExceptionError`](/api/Rascal.Errors.ExceptionError.html) in case an exception is thrown. | ||
|
||
[!code-csharp[](../../samples/Try.csx#L6-L11)] | ||
|
||
<br/> | ||
|
||
`Try` variants also exist for [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) and [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___), namely [`TryMap`](/api/Rascal.Result-1.html#Rascal_Result_1_TryMap__1_System_Func__0___0__) and [`ThenTry`](/api/Rascal.Result-1.html#Rascal_Result_1_ThenTry__1_System_Func__0_Rascal_Result___0___). | ||
|
||
[!code-csharp[](../../samples/TryMap.csx#L6-L11)] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
- name: Samples | ||
href: samples/ | ||
homepage: samples/index.md | ||
- name: API | ||
href: api/ | ||
homepage: api/index.md | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": ".NET Script Debug", | ||
"type": "coreclr", | ||
"request": "launch", | ||
"program": "${env:HOME}/.dotnet/tools/dotnet-script", | ||
"args": ["${file}"], | ||
"windows": { | ||
"program": "${env:USERPROFILE}/.dotnet/tools/dotnet-script.exe", | ||
}, | ||
"cwd": "${workspaceFolder}", | ||
"stopAtEntry": false, | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#r "../src/Rascal/bin/Release/net8.0/Rascal.dll" | ||
|
||
using Rascal; | ||
using static Rascal.Prelude; | ||
|
||
// Read console input and assert that it isn't null. | ||
var name = Console.ReadLine().NotNull(); | ||
|
||
// Read console input, assert that it isn't null, then try parse it into an int. | ||
var age = Console.ReadLine().NotNull() | ||
.Then(str => ParseR<int>(str)); | ||
|
||
// Combine the name and age results together, then map them into a person. | ||
var person = name.Combine(age) | ||
.Map(v => new Person(v.first, v.second)); | ||
|
||
Console.WriteLine(person); | ||
|
||
record Person(string Name, int Age); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#r "../src/Rascal/bin/Release/net8.0/Rascal.dll" | ||
|
||
using Rascal; | ||
using Rascal.Errors; | ||
using static Rascal.Prelude; | ||
|
||
// You can create a result either through explicit Ok/Error functions... | ||
var explicitOk = Ok(new Person("Melody", 27)); | ||
var explicitError = Err<Person>("Could not find person"); | ||
|
||
// ... or through implicit conversions... | ||
Result<Person> implicitOk = new Person("Edwin", 32); | ||
Result<Person> implicitError = new StringError("Failed to find person"); | ||
|
||
record Person(string Name, int Age); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#r "../src/Rascal/bin/Release/net8.0/Rascal.dll" | ||
|
||
using Rascal; | ||
using static Rascal.Prelude; | ||
|
||
var name = "Raymond"; | ||
|
||
// Read console input and try parse it into an int. | ||
// If the input cannot be parsed, the result will be an error. | ||
var age = ParseR<int>(Console.ReadLine()!); | ||
|
||
// Map the age to a new person. | ||
// If the age is an error, the person will also be an error. | ||
var person = age.Map(x => new Person(name, x)); | ||
|
||
Console.WriteLine(person); | ||
|
||
record Person(string Name, int Age); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#r "../src/Rascal/bin/Release/net8.0/Rascal.dll" | ||
|
||
using Rascal; | ||
using static Rascal.Prelude; | ||
|
||
// Read console input and assert that it isn't null. | ||
// If the input is null, the value will be an error. | ||
var name = Console.ReadLine().NotNull(); | ||
|
||
// Chain an operation on the name which will only execute if the name is ok. | ||
var person = name.Then(n => | ||
{ | ||
// Read console input, assert that it isn't null, then try parse it into an int. | ||
var age = Console.ReadLine().NotNull() | ||
.Then(str => ParseR<int>(str)); | ||
|
||
// Map the age into a new person. | ||
return age.Map(a => new Person(n, a)); | ||
}); | ||
|
||
Console.WriteLine(person); | ||
|
||
record Person(string Name, int Age); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#r "../src/Rascal/bin/Release/net8.0/Rascal.dll" | ||
|
||
using Rascal; | ||
using static Rascal.Prelude; | ||
|
||
// Try read console input and use the input to read the specified file. | ||
// If an exception is thrown, the exception will be returned as an error. | ||
var text = Try(() => | ||
{ | ||
var path = Console.ReadLine()!; | ||
return File.ReadAllText(path); | ||
}); | ||
|
||
Console.WriteLine(text); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#r "../src/Rascal/bin/Release/net8.0/Rascal.dll" | ||
|
||
using Rascal; | ||
using static Rascal.Prelude; | ||
|
||
// Read console input and assert that it isn't null. | ||
var path = Console.ReadLine().NotNull(); | ||
|
||
// Try to map the input by reading a file specified by the input. | ||
// If ReadAllText throws an exception, the exception will be returned as an error. | ||
var text = path.TryMap(p => File.ReadAllText(p)); | ||
|
||
Console.WriteLine(text); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#r "../src/Rascal/bin/Release/net8.0/Rascal.dll" | ||
|
||
using System.Text.RegularExpressions; | ||
using Rascal; | ||
using Rascal.Errors; | ||
using static Rascal.Prelude; | ||
|
||
// Read console input, assert that it isn't null, and validate that it matches the regex. | ||
var name = Console.ReadLine().NotNull() | ||
.Validate( | ||
str => Regex.IsMatch(str, "[A-Z][a-z]*"), | ||
_ => "Name can only contain characters a-z and has to start with a capital letter."); | ||
|
||
var person = name.Then(n => | ||
{ | ||
// Read console input, assert that it isn't null, try parse it into an int, then validate that it is greater than 0. | ||
var age = Console.ReadLine().NotNull() | ||
.Then(str => ParseR<int>(str)) | ||
.Validate( | ||
x => x > 0, | ||
_ => "Age has to be greater than 0."); | ||
|
||
return age.Map(a => new Person(n, a)); | ||
}); | ||
|
||
Console.WriteLine(person); | ||
|
||
record Person(string Name, int Age); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"script": { | ||
"enableScriptNuGetReferences": true, | ||
"defaultTargetFramework": "net8.0" | ||
} | ||
} |