Rhombus 1.1.0

dotnet add package Rhombus --version 1.1.0                
NuGet\Install-Package Rhombus -Version 1.1.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.1.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Rhombus --version 1.1.0                
#r "nuget: Rhombus, 1.1.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.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 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,053 4/3/2018
1.0.0 1,038 1/20/2018