RSharp 0.1.2

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

// Install RSharp as a Cake Tool
#tool nuget:?package=RSharp&version=0.1.2                

Code coverage tests Publish Packages codecov NuGet version (Rsharp)

rsharp

Provides functional programming utilities, which should be recognizable for rust developers also using csharp.

Table of contents

Getting started

Installation via Package Manager Console in Visual Studio:

PM> Install-Package RSharp

Installation via .NET CLI:

> dotnet add <TARGET PROJECT> package RSharp

Usage

Option

The Option type is a discriminated union, which can be either Some or None. It is used to represent the possibility of a value not being present.

// implicit conversion from int to Some
private static Option<int> Foo() => 2;

// implicit conversion from null to None
private static Option<int> Bar() => null;

var foo = Foo();
var result = foo.Match(
    some: x => x + 1, // <= should trigger this line
    none: () => 0
);

var bar = Bar();
var result2 = bar.Match(
    some: x => x + 1,
    none: () => 0 // <= should trigger this line
);

Result

The Result type is a discriminated union, which can be either Ok or Err. It is used to represent the possibility of a value not being present.

private static Result<int, Exception> Divide(int a, int b) =>
    b == 0
        ? new DivideByZeroException("Cannot divide by zero")
        : a / b;

var result = Divide(4, 2);

result.Match(
    ok: x => x + 1, // <= should trigger this line
    err: e => 0
);

var result2 = Divide(4, 0);

result2.Match(
    ok: x => x + 1,
    err: e => 0 // <= should trigger this line
);

Like in Rust, the Result type can also be unwrapped by calling the unwrap method. These methods will throw an exception if the Result is an Err.

var result = Divide(4, 2);
var value = result.Unwrap(); // <= should be 2

var result2 = Divide(4, 0);
var value2 = result2.Unwrap(); // <= should throw an exception

There are also methods to unwrap the Result type, but provide a default value if the Result is an Err.

var result = Divide(4, 2);
var value = result.UnwrapOr(0); // <= should be 2

var result2 = Divide(4, 0);
var value2 = result2.UnwrapOr(0); // <= should be 0

Map

The 'Map' function is used to map a single or multiple values to a new value. It is similar to the Select method in LINQ.

To map SourceObject to TargetObject, we use the following function for the examples below.

// Define a factory method to create a new instance of the target object.
private static readonly Func<SourceObject, TargetObject> Factory = source =>
    new TargetObject(new Guid(source.Id.ToString().PadLeft(32, '0')), source.Name, source.Description,
        source.Value);

Note that the Map function returns a Result type, which can be either Ok or Err. If the mapping fails, the Err type will contain the exception that was thrown while mapping.' To get the result, you can either call the Unwrap method, or the UnwrapOr method, or any other of the Unwrap methods.

var a = new SourceObject(1, "Object 1", "This is the first element in the list", 1.0);
var mapResult = a.Map(Factory);

// Use the Match method to get the result, or the Unwrap method to get the result directly.
mapResult.Match(
    ok: b => b, // will be triggered if the mapping succeeds
    err: e => null // will be triggered if the mapping fails
);

You can also map a collection of values to a new value. Since the map function can either fail or succeed, the result will be a collection of Result types. You can use the Unwrap method to get the results that succeeded, or the UnwrapOr method to get the results that succeeded, or a default value if the mapping failed. Or just use the Where method to filter out the results that failed.

// Define the source and target objects.
internal record SourceObject(int Id, string Name, string Description, double Value);
internal record TargetObject(Guid Id, string Name, string Description, double Value);

var a = new List<SourceObject>
        {
            new(-1, "Object 1", "This is the first element in the list", 1.0),
            new(2, "Object 2", "This is the second element in the list", 2.0),
            new(3, "Object 3", "This is the third element in the list", 3.0)
        };
var b = a.Map(Factory).ToList();

// Filter out the errors, should be one item in this case as the first item has an invalid id.
var errors = b.Where(x => x.IsErr()).ToList();
// Get the results that succeeded, should be two items in this case.
var results = b.Where(x => x.IsOk()).Select(x => x.Unwrap()).ToList();

ForEach

The ForEach function is used to iterate over a collection of values. It is similar to the ForEach method in LINQ but adds the index of the current item to the callback function.

var a = new List<SourceObject>
        {
            new(1, "Object 1", "This is the first element in the list", 1.0),
            new(2, "Object 2", "This is the second element in the list", 2.0),
            new(3, "Object 3", "This is the third element in the list", 3.0)
        };
// Use the ForEach function to iterate over the collection and print the values
// to the console along with the index.
a.ForEach((item, index) => Console.WriteLine($"Item {index}: {item}"));

You can also use the ForEach function to generate a new collection of values.

var a = new List<SourceObject>
        {
            new(1, "Object 1", "This is the first element in the list", 1.0),
            new(2, "Object 2", "This is the second element in the list", 2.0),
            new(3, "Object 3", "This is the third element in the list", 3.0)
        };
var b = a.ForEach((item, index) => Factory(item)).ToList();
Assert.Equal(3, b.Count);
// Use the Assert.All method to check if all items in the collection are of the correct type.
Assert.All(b, x => Assert.IsType<TargetObject>(x));
Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  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.
  • net7.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.2 309 4/11/2023
0.1.1 208 4/6/2023
0.0.3 230 3/23/2023
0.0.2 228 3/22/2023
0.0.1 239 3/22/2023

Map can now return both option and result types, based on the result of the function passed to it