Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nillerr/amplified.csharp
Provides a set of types that provide null safety and functional chaining, to enable a functional-like programming style in C#.
https://github.com/nillerr/amplified.csharp
csharp functional-programming monad nuget
Last synced: 4 days ago
JSON representation
Provides a set of types that provide null safety and functional chaining, to enable a functional-like programming style in C#.
- Host: GitHub
- URL: https://github.com/nillerr/amplified.csharp
- Owner: Nillerr
- License: mit
- Created: 2016-12-22T08:30:33.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2019-04-03T18:34:20.000Z (almost 6 years ago)
- Last Synced: 2024-12-18T06:10:49.343Z (21 days ago)
- Topics: csharp, functional-programming, monad, nuget
- Language: C#
- Homepage: https://www.nuget.org/packages/Amplified.CSharp/
- Size: 211 KB
- Stars: 4
- Watchers: 2
- Forks: 1
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Amplified.CSharp
![NuGet](https://img.shields.io/nuget/v/Amplified.CSharp.svg) ![Build status](https://ci.appveyor.com/api/projects/status/penxirmcfh2mhjxt/branch/master?svg=true)
## Installation
The project is available as a [NuGet](https://www.nuget.org/packages/Amplified.CSharp) package.
```
Install-Package Amplified.CSharp
```
## Usage### Static imports
We recommended adding a static import of the following namespace, in files where creation of `Maybe`
instances occur.
```C#
using static Amplified.CSharp.Maybe;
```This allows you to use the library as it was intended. Example:
```C#
using Amplified.CSharp;
...
Maybe foo = Maybe.Some(1);
Maybe bar = Maybe.None();
...
```Becomes:
```C#
using Amplified.CSharp;
using static Amplified.CSharp.Maybe;
...
Maybe foo = Some(1);
Maybe bar = None();
...
```### Unit
`Unit` represents the lack of a value, sort of like `void`. Unlike `void`, it is an actual type, and can
be referenced and operated upon. The difference between `Unit` and `None` is that `None` is actually one
of the possible types of a `Maybe`, whereas `Unit` has no relationship with `Maybe`.#### Examples
```C#
using System;
using Amplified.CSharp;class Program
{
public static void Main(string[] args)
{
var arg1 = args.FirstOrNone().OrThrow(() => new ArgumentException());
Unit ignored = ParseInt(arg1)
.Map(intArg => Store(intArg).Return(intArg))
.Match(
intArg => Console.WriteLine($"Stored: {intArg}"),
none => Console.WriteLine("Invalid input arguments")
);
}
public static Unit Store(int value)
{ ... }
}
```### Maybe
`Maybe` represents the possibility for the presence of a value, sort of like `null` does for reference
types, but it's more like `Nullable`. The idea is to prevent hard to predict runtime errors and
exceptions by forcing you, the developer, to consider all possible cases before operating on the value
within a `Maybe`. It's analogous to a forced `null` check by the runtime.It is a discriminated union / sum type, that consist of 2 types: `Some` and `None`. Due to
the way type inference works in C#, we must create a separate type `None`, which is implicitly convertible
to `Maybe`, without having any type arguments itself. The reason this is required is to support the
`None()` syntax, creating a `None` without specifying a type argument.#### Usage
```C#
using System;
using Amplified.CSharp;class Program {
public static void Main(string[] args)
{
var productId = new ProductId(67748);
GetRelatedProducts(productId)
.Match(
relatedProducts => DisplayRelatedProducts(productId, relatedProducts),
none => DisplayNoRelatedProducts(productId)
);
}
private Unit DisplayNoRelatedProducts(ProductId source)
{
Console.WriteLine($"Product {source} has no related products");
}
private void DisplayRelatedProducts(ProductId source, ProductId[] relatedProducts)
{
var relatedProductsString = string.Join(", ", relatedProducts);
Console.WriteLine($"Product {source} has these related products: {relatedProductsString}");
}private Maybe GetRelatedProducts(ProductId id)
{
return _productRepository.Product(productId)
.Map(product => product.RelatedProducts);
}
private Maybe Product(ProductId id)
{ ... }
}
```#### Some
`Maybe.Some(T value)` represents the presence of a value. It is recommended to use a static import of
`Amplified.CSharp.Maybe` to achieve a fluent functional syntax.#### None
`None` represents the lack of a value. This is comparable to `void`, but unlike `void`, `None` is a value
itself. Every instance of `None` is equal to any other instance of `None`. Other types can achieve `None`
equality by implementing the interface `ICanBeNone`, which e.g. `Maybe` does.There's no difference between using `new None()` vs `default(None)` vs `Maybe.None()`
in terms of equality. Every one of those 3 operations return the same value.#### Async
To support seamless integration with the Task Parallel Library and the `async` / `await` keywords, the type
`AsyncMaybe` was created. This type acts as a deferred `Maybe`, and is really just a wrapper around it,
using `Task` objects.`AsyncMaybe` will be migrated to use the new `ValueTask` in the future.
All methods operating asynchronously has been named with the `Async` suffix, e.g.
```C#
public static AsyncMaybe MapAsync(this Maybe source, Func> mapper);
public static AsyncMaybe MapAsync(this AsyncMaybe source, Func> mapper);
```As you can tell from the method signature in the first line in the block above, whenever you operate
asynchronously on an instance of `Maybe`, an `AsyncMaybe` is returned. In order to return to `Maybe`,
you must simply `await` the instance of `AsyncMaybe`.
```C#
public async Task FetchToken()
{
AsyncMaybe asyncToken = CachedToken()
.OrAsync(() => ObtainToken())
.MapAsync(token => token.ToString())
Maybe token = await asyncToken;
return token.OrReturn("Unable to obtain token");
}public Maybe CachedToken()
{ ... }public async Task ObtainToken()
{
Token token = await ...;
StoreTokenInCache(token);
return token;
}
```#### Converions
```C#
using System;
using System.Threading.Tasks;
using Amplified.CSharp;
using static Amplified.CSharp.Maybe;public void Demonstration()
{
// Maybe
Maybe none = None(); // Implicit conversion from None to Maybe
Maybe noneFromNullable = OfNullable((int?) null);Maybe some = Some(1);
Maybe someFromNullable = OfNullable((int?) 1);
// AsyncMaybe
AsyncMaybe someFromMaybe = Some(1); // Implicit conversion from Maybe to AsyncMaybe
AsyncMaybe someFromTask = Some(Task.FromResult(1));
AsyncMaybe someFromNullable = OfNullable(Task.FromResult(1));
AsyncMaybe noneFromMaybe = None(); // Implicit conversion from None to AsyncMaybe
AsyncMaybe noneFromNullable = OfNullable(Task.FromResult(null));
// Maybe --> AsyncMaybe
Some(1) // Maybe
.{Operator}(...) // Maybe
.{Operator}Async(...) // AsyncMaybe
.ToAsync() // AsyncMaybe
/// AsyncMaybe --> Maybe
AsyncMaybe asyncInt = Some(1).ToAsync();
Maybe nonAsyncInt = await asyncInt;
}
```### LINQ
In order to provide a familiar interface for C# developers, we've provided LINQ-like synonyms for a few
extension methods. We recommend you use the native `Map` and `Fitler` operators, to easily distinquis
between `IEnumerable` / `IQueryable` and `Maybe`.The following methods are synonyms:
```C#
// LINQ synonym for Map
Maybe Select(Func mapper);// LINQ synonym for Filter
Maybe Where(Func predicate);
``````C#
// Asynchronous LINQ synonyms for Map
AsyncMaybe SelectAsync(Func mapper);
AsyncMaybe SelectAsync(Func> mapper);// Asynchronous LINQ synonyms for Filter
AsyncMaybe WhereAsync(Func predicate);
AsyncMaybe WhereAsync(Func> predicate);
```### Null
The `Maybe` type doesn't accept `null` values as parameters. Creating an instance of one of the types,
by passing `null` as the value parameter, will throw an `ArgumentNullException`. Similarly, returning
`null` from a lambda passed to any of the operators returning a `Maybe` will also throw an
`ArgumentNullException`. This means you're allowed to return `null` from lambdas in any of the
terminating operators, such as `Match`, `OrReturn`, `OrGet`, etc.## License
MIT License
Copyright (c) 2016 Nicklas Jensen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.