Rhombus 1.0.0
See the version list below for details.
dotnet add package Rhombus --version 1.0.0
NuGet\Install-Package Rhombus -Version 1.0.0
<PackageReference Include="Rhombus" Version="1.0.0" />
paket add Rhombus --version 1.0.0
#r "nuget: Rhombus, 1.0.0"
// 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 | Versions 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. |
-
.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.