Re: [Haskell-cafe] Not too extensible records?

I have a situation that I think is a good case to apply extensible records to. I have two kinds of entities that are both extensible but in different ways — I would like to make sure their fields are not mixed. At the same time, I want to be able to extend the sets of fields across several modules. Is it possible and is it practical? My case is that I have a type for graphs that is a bifunctor — in node labels and edge labels. So, I need to be able to add more and more fields to labels, but in such a way that it is impossible to assign an unsuitable field.
If your graph is already a bifunctor, why not use a disctinct label or node type in each use case/module? That certainly fits the requirement of not mixing fields. Then you could go the old-fashioned way of type classes, e.g. class HasTopology label where topology :: label -> Topology instance HasTopology Topology where topology = id instance HasTopology label => HasTopology (label,moreInfo) where topology = topology.fst Or for updateable fields, use a Lens Topology. This is a lot of boilerplate, I admit, but you have fine-grained control over what your types have in common. Nested pairs are type- level polymorphic lists and with the right type classes can be used as such, if the list of types is known at compile-time. It is similar to what mtl does with the MonadFoo classes. Olaf

Thank you for your kind suggestions, Olaf and Henning. I am looking into an extensible record based solution for the sake of extensibility and convenience. I could certainly manage everything with a bunch of tuples, but that would require writing a huge number of `HasThis` and `HasThat` instances and even then would not provide the flexibility I should like. For example, suppose I have two functions: one is a layout algorithm that assigns to nodes some spatial positions, and the second is a topological algorithm that discerns roots and leaves. These two functions are conceptually independent and may be applied in any order, but it seems to me that as many as 5 types would be needed to handle this situation: `label`, `(label, V2 Double)`, `(label, Topology)`, `((label, V2 Double), Topology)` and `((label, Topology), V2 Double)`. And what if there is another quality independent of these two? The number of permutations goes through the roof. So, it seems to me that some smart technology is needed to make this manageable.

On Sat, 27 Feb 2021, Ignat Insarov wrote:
I am looking into an extensible record based solution for the sake of extensibility and convenience. I could certainly manage everything with a bunch of tuples, but that would require writing a huge number of `HasThis` and `HasThat` instances and even then would not provide the flexibility I should like.
Before thinking about a new type extension to the already complicated type system with its many extensions I would try to solve the problem with the existing machinery. I think the existing solutions can solve your problem and you do not know whether extensible records would really simplify your code in the end.
For example, suppose I have two functions: one is a layout algorithm that assigns to nodes some spatial positions, and the second is a topological algorithm that discerns roots and leaves. These two functions are conceptually independent and may be applied in any order, but it seems to me that as many as 5 types would be needed to handle this situation: `label`, `(label, V2 Double)`, `(label, Topology)`, `((label, V2 Double), Topology)` and `((label, Topology), V2 Double)`. And what if there is another quality independent of these two? The number of permutations goes through the roof.
What about a record with parameters: data EdgeLabel label vector topology = EdgeLabel {label :: label, vector :: vector, topology :: topology} You can easily disable a field by using the unit type (), i.e. your type my type label EdgeLabel label () () (label, V2 Double) EdgeLabel label (V2 Double) () (label, Topology) EdgeLabel label () Topology ((label, V2 Double), Topology) EdgeLabel label (V2 Double) Topology My experience is that working with a fixed (but maybe large) number of type parameters is much easier than trying to cope with different shapes of nested tuples, extending types, wrappers.

Thank you for your kind suggestions, Olaf and Henning.
I am looking into an extensible record based solution for the sake of extensibility and convenience. I could certainly manage everything with a bunch of tuples, but that would require writing a huge number of `HasThis` and `HasThat` instances and even then would not provide the flexibility I should like.
For example, suppose I have two functions: one is a layout algorithm that assigns to nodes some spatial positions, and the second is a topological algorithm that discerns roots and leaves. These two functions are conceptually independent and may be applied in any order, but it seems to me that as many as 5 types would be needed to handle this situation: `label`, `(label, V2 Double)`, `(label, Topology)`, `((label, V2 Double), Topology)` and `((label, Topology), V2 Double)`. And what if there is another quality independent of these two? The number of permutations goes through the roof.
So, it seems to me that some smart technology is needed to make this manageable. Precisely. And I advocate that this might be ordinary type classes. While the number of combinations is exponential, you'd only write the
On Sat, 2021-02-27 at 19:59 +0500, Ignat Insarov wrote: base and inductive cases for HasFoo. The compiler would figure out the rest. I suggest you look at the machinery in the mtl package. There, a very similar problem is solved: Different types are stacked on top of each other, in any order and depth, and each type provides a certain functionality. Yet in any combination, each functionality in the stack is available at the top level. Olaf
participants (3)
-
Henning Thielemann
-
Ignat Insarov
-
Olaf Klinke