OptRes 6.1.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package OptRes --version 6.1.1                
NuGet\Install-Package OptRes -Version 6.1.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OptRes" Version="6.1.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add OptRes --version 6.1.1                
#r "nuget: OptRes, 6.1.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install OptRes as a Cake Addin
#addin nuget:?package=OptRes&version=6.1.1

// Install OptRes as a Cake Tool
#tool nuget:?package=OptRes&version=6.1.1                

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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.

AsyncApiFileSystem.Kubernetes

Library for a simple api for async long running requests as kubernetes job, using the file system.

MathModel

Concise mathematical modeling library in C#, allowing to code models that are not different nor longer than those on the paper.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
6.1.2 548 1/21/2023
6.1.1 589 1/3/2023
6.1.0 286 1/3/2023
6.0.0 460 12/28/2022