<div dir="ltr">Hi Ignat, as an example of what an extensible records system might look like in Haskell, you could play with Hugs.Trex -- <a href="https://www.haskell.org/hugs/pages/hugsman/exts.html#sect7.2">https://www.haskell.org/hugs/pages/hugsman/exts.html#sect7.2</a><div><br></div><div>To match a few of its features to your post:</div><div><br></div><div>> <span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">suppose I have two functions: one is a layout algorithm </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">that assigns to nodes some spatial positions, and the second is a </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">topological algorithm that discerns roots and leaves. These two </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">functions are conceptually independent and ...</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">    r1 = (label = blah1)</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">    r2 = (topology = blah2 | r1)</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">    r3 = (v2 = blah3 :: Double | r2)</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">The `( ... | r1)` means extend `r1` with some extra field or fields comma-separated. This is an 'extensible' 'anonymous' records system -- 'anonymous' in that you don't pre-declare your record types via `data`. You can name your record types, either with a `type` decl or with a `newtype` that provides a constructor for known-in-advance types.</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">> </span><span style="font-size:1em;color:rgb(0,0,0);font-family:monospace,monospace;white-space:pre-wrap"> may be applied in any </span><span style="font-size:1em;color:rgb(0,0,0);font-family:monospace,monospace;white-space:pre-wrap">order,</span></div><div><span style="font-size:1em;color:rgb(0,0,0);font-family:monospace,monospace;white-space:pre-wrap"><br></span></div><div><span style="font-size:1em;color:rgb(0,0,0);font-family:monospace,monospace;white-space:pre-wrap">Yes you can extend an existing record with arbitrary fields, the result is always a 'flat' tuple with the labels-value pairs forming a set: two records are same type providing they have the same set of labels and the same field types at each label, irrespective of how the labels got added.</span></div><div><span style="font-size:1em;color:rgb(0,0,0);font-family:monospace,monospace;white-space:pre-wrap"><br></span></div><div><span style="font-size:1em;color:rgb(0,0,0);font-family:monospace,monospace;white-space:pre-wrap">> </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">I </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">would like to make sure their fields are not mixed.</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">> </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">I need to be able to add more and more fields to labels, but in such a way </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">that it is impossible to assign an unsuitable field.</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">There's a so-called 'lacks' constraint you can put on record operations </span><span style="font-family:Arial,Helvetica,sans-serif">(written with backslash)</span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">:</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">    foo :: r1\topology => Rec r1 -> a -> Rec (edges :: a | r1)</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"> is the type of a function that will extend record type `r1` lacking label 'topology' with a field `edges` at type `a`.</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">You can put record types with lacks constraints on instances:</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">    instance (r2\v2) => Foo (Rec (label :: String | r2)) </span><span style="font-family:Arial,Helvetica,sans-serif">(Rec (v2 :: Double, label :: String | r2))</span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"> where ...</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">holds for any record that includes label `label` at `String`, and possibly other labels in `r2`, but lacks `v2`. (I'm presuming there's a FunDep on Foo so the second instance parameter is the return type.)</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">> </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"> a huge number </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">of `HasThis` and `HasThat` instances</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">That instance head is in effect a `Has` `label` constraint. There's also a shorthand `#label` that generates a function to extract the value at `label`  **from any record with a `label`** -- that is, Hugs infers the `Has` constraint for you.</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">> </span><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">it seems to me that some smart technology is needed to make this manageable.</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">I'm not seeing Trex as 'smart'. It follows conventional relational algebra semantics; was developed ~1996; last release of Hugs was 2006. I'd have thought GHC would come up with something comparable by now. As Olaf's and Henning's replies show, you could fake it with "a lot of boilerplate" and/or some dubious type trickery.</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap">AntC</span></div><div><span style="color:rgb(0,0,0);font-family:monospace,monospace;font-size:1em;white-space:pre-wrap"><br></span></div></div>