Rhombus 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Rhombus --version 1.0.0
NuGet\Install-Package Rhombus -Version 1.0.0
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="Rhombus" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Rhombus --version 1.0.0
#r "nuget: Rhombus, 1.0.0"
#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 Rhombus as a Cake Addin
#addin nuget:?package=Rhombus&version=1.0.0

// Install Rhombus as a Cake Tool
#tool nuget:?package=Rhombus&version=1.0.0

Rhombus - C# Optics

Included bits and pieces

Lens

A lens represents a special coupling between two types. The first type parameter represents a larger or composed type, that implements a function Get to provide an instance of the second type parameter. (Member Access)

The lens also provides a reverse method, called Set, which given instances of the first type and second type returns a modified first type. (Record Update)

Lens implementation
 public abstract class Lens<T, K> : IOptic<T, K>
{
    public abstract K Get(T from);
    public abstract T Set(K to, T inObject);
}
Lens example
public class ResponsibleExecutiveLens : Lens<Organization,Person>
{
    // get the executive responsible for the organization
    public override Person Get(Organization from) => from.Manager;
    // set the new responsible executive and return a new instance of the organization
    public override Organization Set(Person to, Organization inObject )
    {
        // some menagerial logic here
    }
}

Maybe

The option or maybe type is necessary for using the library successfully. You can use the provided Maybe type implementation or provide your own by deriving IMaybe.

All the option type magic happens around the two constructor methods with different arity.

Maybe implementation

The following implementation uses the IEnumerable interface for accessing values. It can either return an empty list, or a list with exactly one value of type T.

 public class Maybe<T> : IMaybe<T>, IEnumerable<T>
{
    public Maybe() :base()
    { }

    public Maybe(T value) :base(value)
    { }

    public IEnumerator<T> GetEnumerator()
    {
        var result = new List<T>();
        if (HasValue) result.Add(Value);
        return result.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 }
Maybe example
foreach (var i in maybeValue){
    // this code block will execute exactly once or exactly never
}

Prism

Much like the lens, a prism is also a special coupling between two types. However, a prism's Get method may not return a value. This is expressed via the Maybe type.

Prism implementation
public abstract class Prism<T,K> :IOptic<T,K>
 {
    public abstract IMaybe<K> Get(T from);
    public abstract T Set(K to, T inObject);
}

Isomorphism

Also very much like the lens, a isomorphism represents a coupling between types. However, having an isomorphism between types is a much closer coupling meaning that the two types can be interchanged without loss of information.

Isomorphism implementation
public abstract class Isomorphism<T,K> : IOptic<T,K>
{
    public abstract K GetR(T from);
    public abstract T SetL(K from);
}

Epimorphism

Again, the epimorphism is a variation on the original prism, representing a close coupling between two types, with the possibility of failure when interchanging in one direction.

Epimorphism implementation
public abstract class Epimorphism<T,K> : IOptic<T,K>
{
    public abstract IMaybe<K> GetR(T from);
    public abstract T SetL(K from);
}

OpticsBuilder

The OpticsBuilder static class provides reflection-powered methods to instantiate new Lenses, Prisms, Isomorphisms and Epimorphisms.

WARNING! The OpticsBuilder examines it's caller's code to find the necessary implementations. Before calling any OpticsBuilder methods, please first provide implementations for the optics in your codebase (calling assembly). Currently, OpticsBuilder can only find one implementation for a different kind of optic for any a pair of types. Every other call will fail.

OpticsBuilder methods
// static reflective constructors
public static Lens<T,K> BuildLens<T,K>();
public static Prism<T,K> BuildPrism<T,K>();
public static Isomorphism<T,K> BuildIsomorphism<T,K>();
public static Epimorphism<T,K> BuildEpimorphism<T,K>();

// static converters
public static Lens<T, K> ToLens<T, K>(this Isomorphism<T, K> iso) ;
public static Prism<T, K> ToPrism<T, K>(this Epimorphism<T, K> epi);

Optics

The Optics static class provides a unified API for working with optics such as Get, Set and Map.

using static Rhombus.Extensions.Optics; // Get, Set, Map available
// alternatively,
using Rhombus.Extensions; // Optics.Get, Optics.Set, Optics.Map available

Higher order, composition

Rhombus comes equipped with higher order methods allowing for the composition of:

  • Lenses and Lenses with HigherOrder.ComposeLenses
  • Lenses and Prisms with HigherOrder.ComposeLensWithPrism
  • Prisms and Prisms with HigherOrder.ComposePrisms
// tip, use statically
using static Rhombus.HigherOrder;

Decisions for users of the library

How will you implement optics over your data structures and why? What kind of relationships would you like to express? Will you use a Lens or a Prism, or are you confusing Lenses with Isomorphisms?

As a rule of thumb, Rhombus is great tool for the developer when modelling complex domain logic across different projects.

Thanks

I would like to thank the original authors of the F# library Aether, Xyncro Ltd.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.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
1.1.0 1,014 4/3/2018
1.0.0 1,007 1/20/2018