Skip to content

Commit

Permalink
Add samples
Browse files Browse the repository at this point in the history
  • Loading branch information
thinker227 committed Jan 12, 2024
1 parent 7218a17 commit 7b66a32
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 10 deletions.
17 changes: 7 additions & 10 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ NuGet\Install-Package Rascal -IncludePrerelease

# [Script environment](#tab/repl)

In environments such as [dotnet-script](https://github.com/dotnet-script/dotnet-script), [C# REPL](https://github.com/waf/CSharpRepl), or [RoslynPad](https://roslynpad.net), enter:
In environments such as [C# REPL](https://github.com/waf/CSharpRepl) or [RoslynPad](https://roslynpad.net), enter:

```cs
#r "nuget: Rascal"
Expand All @@ -44,15 +44,6 @@ If you wish to install a specific version of the package, specify the package ve
#r "nuget: Rascal, 1.0.1-pre"
```

For the special flavor of C# script mode used by the [MODiX REPL](https://github.com/discord-csharp/CSharpRepl):

```cs
#nuget Rascal
```

> [!NOTE]
> The MODiX REPL currently doesn't support referencing specific package versions and therefore does not support pre-release versions of the package.
# [`PackageReference`](#tab/csproj)

Add under an `ItemGroup` node in your project file:
Expand Down Expand Up @@ -147,6 +138,12 @@ public sealed class UsersController(IUserService userService) : ControllerBase

<br/>

### More samples

A plethora of additional code samples are available in the [samples](/samples/index.html) section of the documentation.

<br/>

### Explore the API

Once you're ready to dive into the library, feel free to refer to the [API documentation](/api/index.html) for an in-depth look into each of the methods provided by the library. You can of course also explore the API through intellisense in your IDE of choice.
Expand Down
64 changes: 64 additions & 0 deletions docs/samples/index.md
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)]
1 change: 1 addition & 0 deletions docs/samples/toc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
3 changes: 3 additions & 0 deletions docs/toc.yml
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
Expand Down
17 changes: 17 additions & 0 deletions samples/.vscode/launch.json
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,
}
]
}
19 changes: 19 additions & 0 deletions samples/Combine.csx
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);
15 changes: 15 additions & 0 deletions samples/Construction.csx
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);
18 changes: 18 additions & 0 deletions samples/Map.csx
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);
23 changes: 23 additions & 0 deletions samples/Then.csx
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);
14 changes: 14 additions & 0 deletions samples/Try.csx
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);
13 changes: 13 additions & 0 deletions samples/TryMap.csx
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);
28 changes: 28 additions & 0 deletions samples/Validation.csx
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);
6 changes: 6 additions & 0 deletions samples/omnisharp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"script": {
"enableScriptNuGetReferences": true,
"defaultTargetFramework": "net8.0"
}
}

0 comments on commit 7b66a32

Please sign in to comment.