Futurum.Core 1.0.16

dotnet add package Futurum.Core --version 1.0.16                
NuGet\Install-Package Futurum.Core -Version 1.0.16                
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="Futurum.Core" Version="1.0.16" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Futurum.Core --version 1.0.16                
#r "nuget: Futurum.Core, 1.0.16"                
#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 Futurum.Core as a Cake Addin
#addin nuget:?package=Futurum.Core&version=1.0.16

// Install Futurum.Core as a Cake Tool
#tool nuget:?package=Futurum.Core&version=1.0.16                

Futurum.Core

license CI Coverage Status NuGet version

A dotnet library providing Option and Result data types, based on the concepts behind 'Railway Oriented Programming'

  • Result data type encapsulating computational result that represents the result that is either a success or a failure
  • Option data type that represents a value that is either present or not present
  • Based on the concepts behind Railway Oriented Programming
  • Extension methods that allows interacting with and going into and out of the Result and Option data types
  • Fluent discoverable and commented API
  • Tested solution > 99% test coverage
  • Tested with Stryker mutation testing > 95%
  • Integration with Polly via Futurum.Core.Polly

Result

Option

Result

The Result data type is a type that represents the result of a computation that can either be a success or a failure. It is a type that is either a success or a failure.

It is a struct, preventing you from returning null instead.

This come in two forms:

Without a payload - Result

var result = Result.Ok();

var result = Result.Fail("Error message");

With a payload - Result<T>

var result = Result.Ok(2);

var result = Result.Fail<int>("Error message");

Any method that can fail should return a Result or Result<T>.

There are extension methods that allow you to easily convert dotnet or third-party dotnet libraries to return Result.

NOTE: There are async version of all the extension methods.

Extension Methods

There are extension methods that allow you to easily convert dotnet or third-party dotnet libraries to return Result.

NOTE: There are async version of all the extension methods.

Combine

Allows you to combine multiple Result's into one, if they have the same payload types.

If there is no payload on the Results passed in:
  • If all of the Results passed are success, then return a Result success
  • If any of the Results passed are failed, then return a Result failure with a ResultError of all of the failure ResultError's
var result1 = Result.Ok();
var result2 = Result.Ok();
var result3 = Result.Ok();

Result result = Result.Combine(result1, result2, result3);
If there are payloads on the Results passed in and they are all the same type:
  • If all of the Results passed are success, then return a Result success with an IEnumerable of all the Result payloads
  • If any of the Results passed are failed, then return a Result failure with a ResultError of all of the failure ResultError's
var result1 = Result.Ok(Guid.NewGuid());
var result2 = Result.Ok(Guid.NewGuid());
var result3 = Result.Ok(Guid.NewGuid());

Result<IEnumerable<Guid>> result = Result.Combine(result1, result2, result3);
If there are IEnumerable payloads on the Results passed in and they are all the same type:
  • If all of the Results passed are success, then return a Result success with an IEnumerable of all the Result payloads concatenated together
  • If any of the Results passed are failed, then return a Result failure with a ResultError of all of the failure ResultError's
var result1 = Result.Ok(new[] {Guid.NewGuid()}.AsEnumerable());
var result2 = Result.Ok(new[] {Guid.NewGuid()}.AsEnumerable());
var result3 = Result.Ok(new[] {Guid.NewGuid()}.AsEnumerable());

Result<IEnumerable<Guid>> result = Result.Combine(result1, result2, result3);
CombineAll

Allows you to combine Result's that have different payload types.

You additionally pass in a selector func.

var result1 = Result.Ok(Guid.NewGuid());
var result2 = Result.Ok(Guid.NewGuid().ToString());

Result<(Guid, string)> result = Result.CombineAll(result1, result2);
Compensate

Allows you to provide a compensating action (that also returns a Result), if the Result is:

  • a failure
var result = Result.Fail<int>("Error message").Compensate(() => Result.Ok(2));
CompensateWhen

Allows you to provide a compensating action (that also returns a Result), if the Result is:

  • a failure of a specific type
  • matches a predicate optional
var result = Result.Fail<int>("Error message").CompensateWhen<InvalidOperationException>(() => Result.Ok(2));
CompensateWhenAny

Allows you to provide a compensating action (that also returns a Result), if the Result is:

  • a failure of a specific type
  • matches a predicate optional

NOTE: this will flatten a ResultErrorComposite.

var result = Result.Fail<int>("Error message").CompensateWhenAny<InvalidOperationException>(() => Result.Ok(2));
Do - Perform side-effect only on success

Executes a side-effect if the Result is success.

var result = Result.Ok(2)
                   .Do(x =>
                   {
                        // do something
                   });
DoWhenFailure - Perform side-effect only on failure

Executes a side-effect if the Result is failure.

var result = Result.Fail<int>("Error message")
                   .DoWhenFailure(x =>
                   {
                        // do something
                   });
DoSwitch - Perform side-effect

Allows you to perform a side-effect on an Result<T>.

You have to say what to do when the value is success and failure.

var result = Result.Fail<int>("Error message")
                   .DoSwitch(x =>
                   {
                        // do something on success
                   },
                   error =>
                   {
                        // do something on failure
                   });
EnhanceWithError

Allows you to provide additional context to a failure.

NOTE: This will create a ResultErrorComposite, with the parent IResultError being the error passed in and the existing IResultError its children.

var result = Result.Fail<int>("Error message").EnhanceWithError("Additional context");
Factory
Result.Ok and Result.Ok<T>
  • Creates a success Result
  • Creates a success Result<T> with the payload provided
var result = Result.Ok();
var result = Result.Ok(2);
Result.Fail
  • Creates a failure Result with the error provided
  • Creates a failure Result<T> with the error provided
var result = Result.Fail("Error message");
var result = Result.Fail<int>("Error message");
ToResultOk - Result → Result<T>

Creates a success Result<T> with the payload provided

var result = 2.ToResultOk();
IgnoreFailure

Ignores the failure on Result and always returns Result.Ok().

var resultInput = Result.Fail(ErrorMessage);

var resultOutput = resultInput.IgnoreFailure();
Map - Result<T> → Result<TR>

Transforms an Result<T> to an Result<TR>.

The transformation will only occur if there is the Result is success.

var result = Result.Ok(1).Map(x => x.ToString());

NOTE It is assumed that the transformation func that you pass in cannot fail, i.e. throw an exception.

NOTE If it can throw an exception, use Then, returning a Result<TR> from the transformation func.

MapSwitch

Transforms an Result<T> to a Result<TR>, allows you to pass in seperate transformations for success and failure.

var result = Result.Ok(1).MapSwitch(x => x + 1, () => 0);
Switch

Allows you to transform an Result<T> to a TR.

You have to say what to return when the value is success and failure.

int result = Result.Ok(1).Switch(x => x + 1, () => 0);
Then - Result<T> → Result<TR>

Allows you to bind two Result or Result<T> methods together.

Result resultInput = Result.Ok();

Result resultOutput = resultInput.Then(Result.Ok);
Result<int> resultInput = Result.Ok(1);

Result<string> resultOutput = resultInput.Then(value => Result.Ok(value.ToString()));
ThenSwitch

Transforms an Result<T> to a Result<TR>

Allows you to specify a predicate to change the way the Result is transformed.

ThenTry

Allows you to execute a func wrapping it in a try / catch

  • If an exception is thrown, it will return a Result.Fail
  • If no exception is thrown it will return a Result.Ok

The result of this is then passed to the then func

var result = await Result.ThenTryAsync(func, () => ERROR_MESSAGE);

A nice pattern to follow using local functions is as follows:

public Task<Result<string>> ExecuteAsync()
{
    return Result.Ok().ThenTryAsync(Execute, () => ERROR_MESSAGE);
    
    Task<string> Execute()
    {
        // Do something
    }
}
public Task<Result<string>> ExecuteAsync()
{
    return Result.Ok().ThenTryAsync(Execute, () => ERROR_MESSAGE);
    
    Task<Result<string>> Execute()
    {
        // Do something
    }
}
public Task<Result> ExecuteAsync()
{
    return Result.Ok().ThenTryAsync(Execute, () => ERROR_MESSAGE);
    
    Task Execute()
    {
        // Do something
    }
}
public Task<Result> ExecuteAsync()
{
    return Result.Ok().ThenTryAsync(Execute, () => ERROR_MESSAGE);
    
    Task<Result> Execute()
    {
        // Do something
    }
}

NOTE

  • This is really Result.Try and Result.Then combined.
  • This can be a good way to integrate third-party libraries.
  • There can be no return value
  • The return value can be a T or a Result<T>
ToNonGeneric - Result<T> → Result

Discards the payload.

var result = Result.Ok(2).ToNonGeneric();
Try

Will run the func wrapping it in a try / catch.

  • If an exception is thrown, it will return a Result.Fail, setting the ResultError to the exception
  • If no exception is thrown it will return a Result.Ok, with the return value if there is one
var result = await Result.TryAsync(func, () => ERROR_MESSAGE);

A nice pattern to follow using local functions is as follows:

With a return value
public Task<Result<string>> ExecuteAsync()
{
    return Result.TryAsync(Execute, () => ERROR_MESSAGE);
    
    Task<string> Execute()
    {
        // Do something
    }
}
With a Result<T> return value
public Task<Result<string>> ExecuteAsync()
{
    return Result.TryAsync(Execute, () => ERROR_MESSAGE);
    
    Task<Result<string>> Execute()
    {
        // Do something
    }
}
With no return value
public Task<Result> ExecuteAsync()
{
    return Result.TryAsync(Execute, () => ERROR_MESSAGE);
    
    Task Execute()
    {
        // Do something
    }
}
With a Result return value
public Task<Result> ExecuteAsync()
{
    return Result.TryAsync(Execute, () => ERROR_MESSAGE);
    
    Task<Result> Execute()
    {
        // Do something
    }
}

NOTE

  • This can be a good way to integrate third-party libraries.
  • There can be no return value
  • The return value can be a T or a Result<T>
TryGetValue - Get a value from a Dictionary

The TryGetValue extension methods work with dictionary.

They get the value from a dictionary as an Result<T>.

var values = Enumerable.Range(0, 10)
                       .ToDictionary(x => x, x => x);

var result = values.TryGetValue(2, ERROR_MESSAGE);
Unwrap - Result<T> → T or Exception

Comes out of the Result monad.

  • If the Result is Ok, then return the value if there is one
  • If the Result is Fail, then throw an exception, passing through the ResultError data
Success - With no return value
Result.Ok().Unwrap();
Success - With a return value
var value = Result.Ok(1).Unwrap();
Failure - With no return value (Exception will be thrown)
Result.Fail(ErrorMessage).Unwrap();
Failure - With a return value (Exception will be thrown)
var value = Result.Fail<int>(ErrorMessage).Unwrap();
Enumerable
Choose

Return only those elements of a sequence of Result<T> that are success.

If none of the elements in the sequence are success, then a Result<T> failure is returned.

var result1 = Result.Ok(1);
var result2 = Result.Ok(2);
var result3 = Result.Ok(3);

IEnumerable<int> values = new[] {result1, result2, result3}.Choose()
Filter

Filters an IEnumerable>Result<T>> based on a predicate

var values = Enumerable.Range(0, 10);

var resultInput = Result.Ok(values);

Result<IEnumerable<int>> resultOutput = resultInput.Filter(x => x % 2 == 0);
FlatMap

Transforms each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence.

(IEnumerable<T>, Func<T, Result>) -> Result
var input = new[] {1, 2};

Result outputResult = input.FlatMap(x => Result.Ok());
(IEnumerable<T>, Func<T, Result<TR>>) -> Result<IEnumerable<TR>>
var input = new[] {1, 2};

Result outputResult = input.FlatMap(x => Result.Ok());
(IEnumerable<T>, Func<T, IEnumerable<TR>>) -> Result<IEnumerable<TR>>
var input = new[] {1, 2};

Result<IEnumerable<int>> outputResult = input.FlatMap(x =>
{
    if (x == 1) return Result.Fail<IEnumerable<int>>(ErrorMessage1);

    return Result.Fail<IEnumerable<int>>(ErrorMessage2);
});
(Result<IEnumerable<T>>, Func<T, Result<IEnumerable<TR>>>) -> Result<IEnumerable<TR>>
var input = new[] {1, 2}.AsEnumerable().ToResultOk();

Result<IEnumerable<int>> outputResult = input.FlatMap(x =>
{
    if (x == 1) return Result.Fail<IEnumerable<int>>(ErrorMessage1);

    return Result.Fail<IEnumerable<int>>(ErrorMessage2);
});
(Result<IEnumerable<T>>, Func<T, Result<TR>>) -> Result<IEnumerable<TR>>
var input = new[] {1, 2}.AsEnumerable().ToResultOk();

Result<IEnumerable<int>> outputResult = input.FlatMap(x =>
{
    if (x == 1) return Result.Fail<int>(ErrorMessage1);

    return Result.Fail<int>(ErrorMessage2);
});
Result<IEnumerable<IEnumerable<T>>> -> Result<IEnumerable<T>>
var input = new[] {1, 2};

Result<IEnumerable<int>> outputResult = input.FlatMap(x => new[] { x * 2 }.AsEnumerable().ToResultOk());

NOTE

  • If ParallelOptions is not specified, then it will use default ParallelOptions.
  • There are overloads that take a ParallelOptions, so you can control the level of concurrency.
FlatMapAs

Transforms an Result<IEnumerable<T>> to a Result<IEnumerable>TR>>

public record Container(IEnumerable<int> Values);
var values = Enumerable.Range(0, 1)
                       .Select(x => new Container(new []{x}));

Result<IEnumerable<Container>> resultInput = Result.Ok(values);

Result<IEnumerable<int>> resultOutput = resultInput.FlatMapAs(x => x.Values, x -> x * 2);
FlatMapSequential

Transforms each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence.

NOTE This runs sequentially, i.e. runs with a ParallelOptions MaxDegreeOfParallelism = 1

var input = new[] { 1, 2 };

Result outputResult = await input.FlatMapSequentialAsync(x =>
{
    if (x == 1) return Result.FailAsync(ErrorMessage1);

    return Result.FailAsync(ErrorMessage2);
});
FlatMapSequentialUntilFailure

Transforms each element of a sequence to an IEnumerable<T>, returning the first error it finds.

var input = new [] {1,2, 3};

Result outputResult = input.FlatMapSequentialUntilFailure(x => x == 2 ? Result.Fail(ErrorMessage1) : Result.Ok());
MapAs

Transforms an Result<IEnumerable<T>> to a Result<IEnumerable>TR>>

var values = Enumerable.Range(0, 1);

var resultInput = Result.Ok(values);

Result<IEnumerable<int>> resultOutput = resultInput.MapAs(x => x + 1);
Pick

Return the first element of a sequence of Result<T> that are success.

If none of the elements in the sequence match are success, then a Result<T> failure is returned.

var result1 = Result.Ok(1);
var result2 = Result.Ok(2);
var result3 = Result.Ok(3);

Result<int> result = new[] {result1, result2, result3}.Pick(ErrorMessage);
PickFailureOrSuccess

Return the first element of a sequence of Result that are failure.

If none of the elements in the sequence match are failure, then a Result failure is returned.

var result1 = Result.Ok();
var result2 = Result.Ok();
var result3 = Result.Ok();

Result result = new[] {result1, result2, result3}.PickFailureOrSuccess();
ThenAs

Transforms an Result<IEnumerable<T>> to a Result<IEnumerable>TR>>

var values = Enumerable.Range(0, 1);

var resultInput = Result.Ok(values);

Result<int> resultOutput = resultInput.ThenAs(x => (x + 1).ToResultOk());
TryFirst

Sequentially runs through a sequence of elements applying a function that returns a Result<TR>. The first one that is success is returned.

var input = new[] { 1, 2, 3 };

Result<string> result = input.TryFirst(x =>
                            {
                                if (x == 1)
                                {
                                    return Result.Fail<string>(ErrorMessage);
                                }

                                return x.ToString().ToResultOk();
                            },
                            ErrorMessage);
TryToDictionary

Try to create a Dictionary<TKey, TElement;> from an IEnumerable<T> according to specified key selector and element selector functions.

NOTE: This will explicitly checks for duplicate keys and return a Result<Dictionary<TKey, TElement>> failure if there are any.

var numbers = Enumerable.Range(0, 10)
                        .ToList();

Result<Dictionary<int, int>> result = numbers.TryToDictionary(x => x, x => x);
AsyncEnumerable
Choose

Return only those elements of a sequence of Result<T> that are success.

If none of the elements in the sequence are success, then a Result<T> failure is returned.

IAsyncEnumerable<Result<int>> inputResults = AsyncEnumerable.Range(1, 3)
                                                            .Select(x => Core.Result.Result.Ok(x));

List<int> values = await inputResults.Choose().ToListAsync();
Filter

Filters an IEnumerable>Result<T>> based on a predicate

var values = AsyncEnumerable.Range(0, 1);

var resultInput = Result.Ok(values);

Result<IAsyncEnumerable<int>> returnedResult = resultInput.Filter(x => x % 2 == 0);
MapAs

Transforms an Result<IEnumerable<T>> to a Result<IEnumerable>TR>>

var values = AsyncEnumerable.Range(0, 1);

var resultInput = Result.Ok(values);

Result<IAsyncEnumerable<int>> returnedResult = resultInput.MapAs(x => x * 2);
Interop with Option
Option → Result

You can move between an Option and a Result using the 'ToResult' extension method. You just need to provide the error if the Option is not present.

var option = "hello".ToOption();

var result = option.ToResult(ErrorMessage);
Result → Option

This is not supported as it would result in the loss of the error context.

ResultError

Failures are represented as by the IResultError interface. These represent the context of the error.

This interface has two implementations:

  • IResultErrorNonComposite
  • IResultErrorComposite
IResultErrorNonComposite

This is the base interface for all IResultError implementations that are not composite, i.e. have no children.

IResultErrorComposite

This is the base interface for all IResultError implementations that are composite, i.e. have children.

What ResultError's come built-in

There are a number of built-in IResultError implementations.

You are free to create your own, by implementing either the IResultErrorNonComposite or IResultErrorComposite interfaces.

ResultErrorMessage

Represents a string error message, without any other context.

You can transform a string to a ResultErrorMessage using the extension method ToResultErrorMessage.

var resultError = "error message".ToResultError();

NOTE Most extension methods have an overload that allows you to pass in a error string.

ResultErrorException

Represents a exception error.

You can transform an Exception to a ResultErrorMessage using the extension method ToResultErrorMessage.

var resultError = new Exception("error message").ToResultError();
ResultErrorEmpty

Represents an empty error message, this is mainly used internally in the library.

var resultError = ResultErrorEmpty.Value;
ResultErrorComposite

This is a special IResultError, it allows you to have a tree structure of IResultError's.

var resultError = resultErrors.ToResultError();
var resultError = ResultErrorCompositeExtensions.ToResultError(parentResultError, resultErrors);
What to do with them

There are four supported ways to get the data from an IResultError:

  • GetErrorString
  • GetErrorStringSafe
  • GetErrorStructure
  • GetErrorStructureSafe
GetErrorString

This gets a string representing the error(s). Different implementation of IResultError deal with this differently.

ResultErrorComposite will call GetErrorString on all of their children, separating them with the specified separator.

GetErrorStringSafe

This gets a string representing the error(s). Different implementation of IResultError deal with this differently.

ResultErrorComposite will call GetErrorStringSafe on all of their children, separating them with the specified separator.

NOTE: The error message will be safe to return to the client, that is, it will not contain any sensitive information e.g. StackTrace.

GetErrorStructure

This gets a tree structure representing the error(s). This tree structure is serializable to Json.

ResultErrorComposite will call GetErrorStructure on all of their children, and add them as children to the ResultErrorComposite.

GetErrorStructureSafe

This gets a tree structure representing the error(s). This tree structure is serializable to Json.

ResultErrorComposite will call GetErrorStructureSafe on all of their children, and add them as children to the ResultErrorComposite.

NOTE: The error message will be safe to return to the client, that is, it will not contain any sensitive information e.g. StackTrace.

Option

The Option data type is a type that represents a value that is either present or not present. It is a type that is either HasValue or HasNoValue.

It is a struct, preventing you from returning null instead.

It can be used for value and reference types.

Extension Methods

Map - Option<T> → Option<TR>

Allows you to transform an Option<T> to an Option<TR>.

The transformation will only occur if there is a value present.

var option = Option.From(1).Map(x => x + 1);
Switch - Option<T> → TR

Allows you to transform an Option<T> to a TR.

You have to say what to return when the value is present and not present.

var option = Option.From(1).Switch(x => x + 1, () => 0);
DoSwitch - Perform side-effect

Allows you to perform a side-effect on an Option<T>.

You have to say what to do when the value is present and not present.

var option = Option.From(1).Switch(x =>
                                  {
                                        // do something when the value is present
                                  }, 
                                  () => 
                                  {
                                        // do something when the value is not present
                                  });
TryGetValue - Get a value from a Dictionary

Gets the value from a dictionary as an Option<T>.

var values = Enumerable.Range(0, 10)
                       .ToDictionary(x => x, x => x);

var option = values.TryGetValue(2);
Factory - T → Option<T>

There are a number of factory extension methods.

From

Converts a T to an Option<T>

If the value is not null, then it will be a Option<T> with the value on it.

var option = Option.From(1);
var option = Option.From("hello");

If the value is null, then it will be a Option<T> with the value not set.

var option = Option.From((int?)null);
var option = Option.From((object)null));
None

Returns a Option<T> with the value not set.

var option = Option<int>.None;
var option = Option<string>.None;
ToOption

Converts a T to an Option<T>

If the value is not null, then it will be a Option<T> with the value on it.

var option = 1.ToOption();
var option = "hello".ToOption();

If the value is null, then it will be a Option<T> with the value not set.

var option = ((int?)null).ToOption();
var option = ((object)null).ToOption();
Implicit conversion

There is an implicit conversion in place. So if your method returns an T, then it will be automatically converted to an Option<T>.

OrElse and GetValueOrDefault

The OrElse extension methods are good when you have a precedence order.

The GetValueOrDefault extension methods are used to as a terminating method and will take you out if an Option, returning the default value provided if the Option has no value.

var value = Option.From(1)
                  .OrElse(Option.From(2))
                  .OrElse(Option.From(3))
                  .GetValueOrDefault(4);
TryParse

There are a lot of TryParse extension methods provided, for a lot of the built-in framework types:

Bool
var option = true.ToString().TryParseBool();
Int
var option = 1.ToString().TryParseInt();
Long
var option = 1.ToString().TryParseLong();
Decimal
var option = 1m.ToString().TryParseDecimal();
Double
var option = 1d.ToString().TryParseDouble();
Float
var option = 1f.ToString().TryParseFloat();
Guid
var option = Guid.NewGuid().ToString().TryParseGuid();
DateTime
var option = DateTime.Now.ToString().TryParseDateTime();
DateOnly
var option = DateOnly.FromDateTime(DateTime.Now).ToString().TryParseDateOnly();
TimeOnly
var option = TimeOnly.FromDateTime(DateTime.Now).ToString().TryParseTimeOnly();
Enum
var option = EnumValues.Value2.ToString().TryParseEnum<EnumValues>();
Enumerable<Option<T>>
Choose

Return only those elements of a sequence of Option<T> that have a value.

var options = Enumerable.Range(0, 10).Select(i => i.ToOption());

var returnValues = options.MapSwitch(value => trueValue, () => falseValue);
Map

Transforms each element of a sequence into a new form.

The transformation will only occur if there is a value present.

var options = Enumerable.Range(0, 5).Select(i => i.ToOption());

var values = options.Map(x => x + 1);
MapSwitch

Transforms each element of a sequence based, allows you to pass in seperate transformations for if the value is there or not.

var options = Enumerable.Range(0, 10).Select(i => i.ToOption());

var returnValues = options.MapSwitch(value => trueValue, () => falseValue);
Pick

Return the first element of a sequence based on if the value is there.

If none of the elements in the sequence match the predicate, then Option<T>.None is returned.

var option1 = Core.Option.Option.From(1);
var option2 = Core.Option.Option.From(2);
var option3 = Core.Option.Option.From(3);

var option = new[] {option1, option2, option3}.Pick();
TryElementAt

Returns the element at a specified index in a sequence.

  • If the index is out of range, then an Option<T>.None is returned.
  • If the index is in range, the element at the specified position in the source sequence is returned as a Option<T>.
var option = Enumerable.Range(0, 10).TryElementAt(2);
TryFirst

Returns the first element of a sequence.

  • If the sequence is empty, then an Option<T>.None is returned.
  • If the sequence is not empty, the first element is returned as a Option<T>.
var option = Enumerable.Range(0, 10).TryFirst();
TryLast

Returns the last element of a sequence.

  • If the sequence is empty, then an Option<T>.None is returned.
  • If the sequence is not empty, the last element is returned as a Option<T>.
var option = Enumerable.Range(0, 10).TryLast();
TrySingle

Returns the only element of a sequence.

  • If the sequence is empty, then an Option<T>.None is returned, wrapped in a Result<T>.IsSuccess.
  • If the sequence has 1 value, then the value is returned as a Option<T>, wrapped in a Result<T>.IsSuccess.
var option = Enumerable.Range(0, 10).TrySingle();
Interop with Nullable

For structs there is interop with Nullable

Nullable → Option

Use the ToOption extension method.

var option = ((int?)null).ToOption();
var option = ((int?)1).ToOption();
Option → Nullable

Use the ToNullable extension method.

int? value = null;

var option = value.ToOption();

var nullable = option.ToNullable();
int? value = 1;

var option = value.ToOption();

var nullable = option.ToNullable();
Interop with Result
Option → Result

You can move between an Option and a Result using the 'ToResult' extension method. You just need to provide the error if the Option is not present.

var option = "hello".ToOption();

var result = option.ToResult(ErrorMessage);
Result → Option

This is not supported as it would result in the loss of the error context.

Stryker mutation testing

Make sure you have Stryker installed

dotnet tool install -g dotnet-stryker

Make sure the Test project is built

dotnet build ./test/Futurum.Core.Tests

Run Stryker

dotnet stryker
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.

NuGet packages (7)

Showing the top 5 NuGet packages that depend on Futurum.Core:

Package Downloads
Futurum.WebApiEndpoint

A dotnet library that allows you to build WebApiEndpoints using a vertical slice architecture approach. Provides a structured way to create REST apis in dotnet without controllers.

Futurum.Test

Small dotnet testing library, allowing you to test code that uses Futurum.Core

Futurum.FluentValidation

A dotnet library, that allows FluentValidation to work with Futurum.Core.

Futurum.ApiEndpoint

A dotnet library, that is the base for ApiEndpoints in Futurum.

Futurum.Core.Polly

Small dotnet library, allowing you to use [Polly](https://github.com/App-vNext/Polly) with Futurum.Core, based on the concepts behind 'Railway Oriented Programming'.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.16 526 4/10/2023
1.0.15 392 4/7/2023
1.0.14 354 3/26/2023
1.0.13 809 11/18/2022
1.0.12 431 11/3/2022
1.0.11 1,279 6/3/2022
1.0.10 566 4/4/2022
1.0.9 421 4/3/2022
1.0.8 577 3/21/2022
1.0.7 401 3/19/2022
1.0.6 1,701 2/24/2022
1.0.5 545 2/23/2022
1.0.4 421 2/20/2022
1.0.3 1,106 2/2/2022
1.0.2 428 1/28/2022
1.0.1 2,128 1/28/2022