OptRes 6.1.2
dotnet add package OptRes --version 6.1.2
NuGet\Install-Package OptRes -Version 6.1.2
<PackageReference Include="OptRes" Version="6.1.2" />
paket add OptRes --version 6.1.2
#r "nuget: OptRes, 6.1.2"
// Install OptRes as a Cake Addin #addin nuget:?package=OptRes&version=6.1.2 // Install OptRes as a Cake Tool #tool nuget:?package=OptRes&version=6.1.2
OptRes
C# library for option and lightweight-result types:
Opt<T>
: Some(T) or None;Res
: Ok or Err(message);Res<T>
: Ok(T) or Err(message).
In order to enable the types glboally, add the following in project's global usings file:
global using OptRes;
global using static OptRes.Extensions;
Alternatively, the scope can be limited to a file by adding the following in the particular file:
using OptRes;
using static OptRes.Extensions;
Complete auto-generated documentation can be found here: sandcastle-documentation.
Opt<T>
Opt<T> is a readonly struct that can be either of the two variants:
- Some(T): holding a nonnull (!) value of T, or
- None: nothing, absense of data.
Despite of the improvements in the language with nullable, the checks are still in warning level.
The type Opt<T> aims to:
- make absense of data and optionality of a method argument explicit; and,
- getting closer to fluent, railway oriented programming in C# (see Scott Wlaschin's talk).
Some of the features are illustrated in the Player
record below; the code can be found in Examples/Player.cs.
record Player(
string Name,
int Wins,
Opt<string> Nickname = default, // stating explicitly that not all players have a nickname
Opt<string> EmailAddress = default // default of Opt<T> is None<T>()
)
{
public void Greet()
{
// UnwrapOr returns the underlying value if IsSome; the fallback value otherwise.
// When the fallback value is expensive (requires a database query, for instance), lazy version can be used:
// Nickname.UnwrapOr(() => Name)
string greeting = string.Format("Hey {0}", Nickname.UnwrapOr(Name));
Console.WriteLine(greeting);
// alternative uses of UnwrapOr, Match, IsSome & Unwrap
Assert(Nickname.UnwrapOr(Name) == Nickname.Match(whenSome: nick => nick, whenNone: Name));
Assert(Nickname.UnwrapOr(Name) == (Nickname.IsSome ? Nickname.Unwrap() : Name));
}
public bool HasNickname()
=> Nickname.IsSome; // = !IsNone
public Opt<int> NicknameLength()
{
// Map into other types by chaining results, while the IsNone checks are internally handled:
// * if Nickname.IsSome, the result will be the Some(Nickname.Unwrap().Length);
// * None<int>() otherwise.
return Nickname.Map(nick => nick.Length);
}
public void SendEmail(string message)
{
// send only if the player has an email address
EmailAddress.Do(emailAddr => Console.WriteLine($"fake-sending {message} to {emailAddr}"));
}
public int Score()
{
// assume having a nickname provides +5 wins.
return Wins + Nickname.Match(whenSome: _ => 5, whenNone: 0);
}
public void RemindToAddEmail()
{
// do only if IsNone
EmailAddress.DoIfNone(() => Console.WriteLine("you may add your email address for ..."));
}
public Opt<Player> FindMatch(IEnumerable<Player> others)
{
// linq alternative to FirstOrDefault that would return null in absent case.
// there also exist the following extension variants on IEnumerable<Opt<T>>:
// * Do & DoIfNone
// * Map & FlatMap
// * Try & TryMap
// * Match & MatchDo
return others.FirstOrNone(x => x.Score() == Score());
}
public Opt<string> FindNicknameOfMatch(IEnumerable<Player> others)
{
// FindMatch(others).Map(x => x.Nickname) => would have returned Opt<Opt<string>>.
// FlatMap allows to escape the nesting
// Alternatively, one can use the Flatten method:
// FindMatch(others).Map(x => x.Nickname).Flatten();
return FindMatch(others).FlatMap(x => x.Nickname);
}
public Opt<char> FindInitialOfNicknameOfMatch(IEnumerable<Player> others)
{
// you may keep chaining:
// * if any step returns Non, it will be carried on to the end bypassing succeeding methods
// * the output will be IsSome only if all steps return Some.
return FindMatch(others)
.FlatMap(x => x.Nickname)
.Map(nick => nick[0]);
}
// static methods
public static Opt<string> ValidateEmail(string inputEmailAddress)
{
// the value will be Some only if the validation condition is true.
return SomeIf(inputEmailAddress.Contains('@'), inputEmailAddress);
}
public static void Parsers()
{
// string & ReadOnlySpan<char> has ParseOrNone extensions for primitives
Opt<int> nbGames = "12".ParseIntOrNone();
Opt<bool> isWin = "false".ParseBoolOrNone();
Opt<double> elapsedSeconds = "42.42".ParseDoubleOrNone();
// and general parser for any T with lambda parser
Opt<bool> isWinLambda = "Yes".TryParseOrNone(str => StringComparer.OrdinalIgnoreCase.Equals(str, "yes"));
}
public static List<string> Nicknames(IEnumerable<Player> players)
{
// will create a list of all nicknames, skipping None's.
return players.Select(x => x.Nickname).UnwrapSomes().ToList();
}
public static Opt<Player> GetPlayerByNickname(Dictionary<string, Player> dictNickPlayer, string nickname)
{
// (similar to FirstOrNone or LastOrNone)
// alternative to GetValueOrDefault method which would have returned null in absent case.
return dictNickPlayer.GetOpt(nickname);
}
// static methods - constructors
public static Player Dendi()
{
return new Player(
Name: "Dendi",
Wins: 0,
Nickname: Some("NaVi"), // static constructor for Some variant.
EmailAddress: None<string>() // static constructor for None variant; can also use default
);
}
}
Res and Res<T>
Res and Res<T> are simplified result types, where the error variant holds only the error message.
Res has the following two variants:
- Ok: just a flag stating that the state is okay,
- Err(message): an error state with the corresponding error message.
Res<T>, on the other hand, can hold data and has the following variants:
- Ok(T): okay state holding a non-null (!) value of T,
- Err(message): an error state with the corresponding error message.
These result types aim to:
- make possibility of failures explicit;
- avoiding (hiding) the verbose try-catch blocks, as well as, throwing exceptions,
- getting closer to fluent, railway oriented programming in C#.
Some of the features are illustrated in the example files Examples/ExampleRes.cs and Examples/ExampleResT.cs.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- No dependencies.
NuGet packages (6)
Showing the top 5 NuGet packages that depend on OptRes:
Package | Downloads |
---|---|
AsyncApiFileSystem
Library for a simple api for async long running requests, using the file system. |
|
ExcelToCsv
Simple excel table to csv converter. |
|
Fun.Collections.UniJagged
Jagged array representations, wrapping different underlying data types by a functional approach in order to achieve a unified collection type. |
|
MathModel
Concise mathematical modeling library in C#, allowing to code models that are not different nor longer than those on the paper. |
|
AsyncApiFileSystem.Kubernetes
Library for a simple api for async long running requests as kubernetes job, using the file system. |
GitHub repositories
This package is not used by any popular GitHub repositories.