diff --git a/docs/index.md b/docs/index.md
index 7674b55..a61659b 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -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"
@@ -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:
@@ -147,6 +138,12 @@ public sealed class UsersController(IUserService userService) : ControllerBase
+### More samples
+
+A plethora of additional code samples are available in the [samples](/samples/index.html) section of the documentation.
+
+
+
### 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.
diff --git a/docs/samples/index.md b/docs/samples/index.md
new file mode 100644
index 0000000..9c30202
--- /dev/null
+++ b/docs/samples/index.md
@@ -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).
+
+
+
+## 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)]
+
+
+
+## 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)]
+
+
+
+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)]
+
+
+
+[`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`](/api/Rascal.Result-1.html) type, allowing for chaining multiple operations on a single result. In functional terms, these are what makes [`Result`](/api/Rascal.Result-1.html) a functor and monad respectively (although not an applicative).
+
+
+
+> [!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.
+
+
+
+### 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)]
+
+
+
+## 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)]
+
+
+
+## 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)]
+
+
+
+`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)]
diff --git a/docs/samples/toc.yml b/docs/samples/toc.yml
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/docs/samples/toc.yml
@@ -0,0 +1 @@
+[]
diff --git a/docs/toc.yml b/docs/toc.yml
index 7385472..c378383 100644
--- a/docs/toc.yml
+++ b/docs/toc.yml
@@ -1,3 +1,6 @@
+- name: Samples
+ href: samples/
+ homepage: samples/index.md
- name: API
href: api/
homepage: api/index.md
diff --git a/samples/.vscode/launch.json b/samples/.vscode/launch.json
new file mode 100644
index 0000000..305245b
--- /dev/null
+++ b/samples/.vscode/launch.json
@@ -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,
+ }
+ ]
+}
\ No newline at end of file
diff --git a/samples/Combine.csx b/samples/Combine.csx
new file mode 100644
index 0000000..3dbd10e
--- /dev/null
+++ b/samples/Combine.csx
@@ -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(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);
diff --git a/samples/Construction.csx b/samples/Construction.csx
new file mode 100644
index 0000000..bbad9fa
--- /dev/null
+++ b/samples/Construction.csx
@@ -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("Could not find person");
+
+// ... or through implicit conversions...
+Result implicitOk = new Person("Edwin", 32);
+Result implicitError = new StringError("Failed to find person");
+
+record Person(string Name, int Age);
diff --git a/samples/Map.csx b/samples/Map.csx
new file mode 100644
index 0000000..eaa15e7
--- /dev/null
+++ b/samples/Map.csx
@@ -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(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);
diff --git a/samples/Then.csx b/samples/Then.csx
new file mode 100644
index 0000000..e801721
--- /dev/null
+++ b/samples/Then.csx
@@ -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(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);
diff --git a/samples/Try.csx b/samples/Try.csx
new file mode 100644
index 0000000..a67db88
--- /dev/null
+++ b/samples/Try.csx
@@ -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);
diff --git a/samples/TryMap.csx b/samples/TryMap.csx
new file mode 100644
index 0000000..bcbb803
--- /dev/null
+++ b/samples/TryMap.csx
@@ -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);
diff --git a/samples/Validation.csx b/samples/Validation.csx
new file mode 100644
index 0000000..a79d587
--- /dev/null
+++ b/samples/Validation.csx
@@ -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(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);
diff --git a/samples/omnisharp.json b/samples/omnisharp.json
new file mode 100644
index 0000000..3789428
--- /dev/null
+++ b/samples/omnisharp.json
@@ -0,0 +1,6 @@
+{
+ "script": {
+ "enableScriptNuGetReferences": true,
+ "defaultTargetFramework": "net8.0"
+ }
+}