Rhombus 1.1.0
dotnet add package Rhombus --version 1.1.0
NuGet\Install-Package Rhombus -Version 1.1.0
<PackageReference Include="Rhombus" Version="1.1.0" />
paket add Rhombus --version 1.1.0
#r "nuget: Rhombus, 1.1.0"
// Install Rhombus as a Cake Addin #addin nuget:?package=Rhombus&version=1.1.0 // Install Rhombus as a Cake Tool #tool nuget:?package=Rhombus&version=1.1.0
Rhombus - C# Optics and EnumerableProducts
What started as a simple port of an optics library is now a library on it's own!
Rhombus 1.1 consists of the following two interesting namespaces:
- Rhombus.Optics (provides lenses, prisms, isomorphisms and epimorphisms for accessing data in a functional way)
- Rhombus.Products (built with optics as an example, provides a solution for working with IEnumerables)
Rhombus.Optics
Optics are a collective term for an approach in dealing with immutable data (or complex mutable object hierarchies). In general, optics provide methods akin to . member access and record update. There are many kinds of possible optics, and indeed you will implement many on your own while using this library, however, this library provides only 4 basic types.
- Lens<T,K> - get returns K, set returns a new T given an old T and a new K
- Prism<T,K> - get will maybe return a K, set returns a new T given an old T and a new K
- Isomorphism<T,K> - get returns K, set returns T (notice the symmetry - this is what we ❤️)
- Epimorphism<T,K> - get will maybe return a K, set returns T
All of the above are abstract classes, which means that you, the library user, will have to implement them over your own types. This means that you will be the one choosing the T's and K's for the optics. When you are happy with what you write, you can use the static reflective builders from this library to instantiate your optics.
an example lens
using Rhombus.Optics;
// something you would write
// Note: a convention is to suffix optics with their name or _
public class OrganizationManagerLens : Lens<Organization,Person>
{
public override Person Get(Organization from) => from.Manager;
public override Organization Set(Manager to, Organization inObject)
{
// handle managerial logic ;)
}
}
// somewhere else in your codebase, but in the same assembly (important for reflection!)
var changeManagementHelper = OpticsBuilder.BuildLens<Organization,Person>();
var personToFire = changeManagementHelper.Get(clients_organization);
Note that, in the example above, if there were more lenses in the codebase that correspond to Lens<Organization,Person> the call would fail. This is because the library is still in early development, but we plan to support multiple optics of the same type together with an IQueryable for narrowing down the wanted instance for version 2.0.
Extension methods
To help you manage your optics the library also provides the following static extension methods:
using Rhombus.Optics.Extensions
// or
using static Rhombus.Optics.Extensions.Optics;
- Optics.Get - get from any optic type
- Optics.Set - set in any optic type
- Optics.Map - map over any optic type
Do use them with your own optics!
Composition
Also in the Rhombus.Optics namespace, are the higher order methods for composing lenses, prisms, isos and epis together.
Rhombus.Products
Let's use optics to manipulate IEnumerables! Introducing the EnumerableProduct class.
using System.Linq;
using Rhombus.Products;
// given some enumerables
IEnumerable<UserId> newIds = ...
IEnumerable<NewUser> newUsers = ...
// why not combine them? (just make sure the lengths will match, or information loss)
// doesn't really do anything, it's an IEnumerable<(T1,T2)> in disguise
var usersToRegister = new EnumerableProduct<UserId,NewUser>(newIds, newUsers);
// this will surely combine them
var registered = usersToRegister.Combine(pair => RegisterNewUser(pair.Item2, pair.Item1));
// no silly, those are IEnumerables, remember? We need to enumerate them
var newUsers = Enumerable.ToList(registered);
Also available in the IEnumerable<(T1,T2,T3)> variety, ehem, the EnumerableProduct class, that is.
Rhombus.Products.Lenses
You're thinking, "that's some trickery, no optics there", and you're right. We'll use optics for workloads harder than combining IEnumerables into cartesian products of IEnumerables.
How about an isomorphism for transforming IEnumerables to something and back?
using System.Linq;
using Rhombus.Products;
using Rhombus.Products.Lenses;
var contract = new EnumerableTransformingIsomorphism<(UserId,NewUser),User>(
forward: pair => RegisterNewUser(pair.Item2, pair.Item1),
backward: user => (user.Id, NewUser.Empty)
);
var Registrator = contract.Forward;
IEnumerable<UserId> newIds = ...
IEnumerable<NewUser> newUsers = ...
var registered = Registrator(new EnumerableProduct<UserId,NewUser>(newIds, newUsers));
var evilUsers = new List<User>();
foreach (var newUser in registered) // what is the type of the newUser here? c'mon
if (UserIsEvil(newUser)) evilUsers.Add(newUser);
var RegistrationInvalidator = contract.Backward;
var idsToRelease = RegistrationInvalidator(evilUsers).Left().ToList();
Rhombus.Next
There must be some other use cases for optics over IEnumerables. Send a PR or issue!
What would you like to see next in the Rhombus library? I'm planning a reasoner over optics, more optics over enumerables, more fiddling with cartesian products. I'd really like to have a testing suite for optics, that would significantly speed up development. Luvs.
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.