[haskell-beginners] Lifting over record syntax
Anthony Clayden <[email protected]> Thu, 1 Nov 2018 00:31:06 +1300
| Newsgroups | gmane.comp.lang.haskell.hugs.user |
|---|---|
| Message-ID | <CAM7nRYSkXSuT3emStLFczqAAfefTVsvHxCmp_L9QhRUYeA+_qw@mail.gmail.com> |
--===============8518028055001502429== Content-Type: multipart/alternative; boundary="000000000000043941057984a48c" --000000000000043941057984a48c Content-Type: text/plain; charset="UTF-8" musing on this https://mail.haskell.org/pipermail/beginners/2018-October/018363.html to which my reply talked mainly about Trex. Does it make sense to regard a Trex record (or any free-standing anonymous/extensible record) as a container, with values 'lifted' into the structure, and an interface given by the label names, _not_ by order of fields? That is, "container" in the Monad sense? Does it further make sense if all the values are already lifted into some Functor (like Maybe or (Either e) ) to hoist the Functor out over the record structure? I'm not sure I'm up with the program for Functor/Applicative/Monad everywhere. If a record structure is a Functor, what's the type constructor? `Rec`? If record structure is an Applicative, what's the constructor for `pure` that lifts into `Rec`? The `<*>` aka `ap` or bind for a Monad presumably is to take two records (with disjoint labels) and concatenate them to a single record. Given data Person = Person (Rec( name :: String, age :: Int)) we want, corresponding to the question: Person <?$> (age = 27) <*> (name = "Joe") in which operator <?$> is map-like: it takes a function to its left that expects a record of n fields, then maps over n singleton records with corresponding labels appearing in any order. I might be able to get there by generics/reflection over the field names embedded in data constructor `Person`s type, then overloading <?$> to look for each label. That is, if Hugs had any sort of generics/TypeRep. Label names in Trex are literals; there's no such thing as a label variable. (Which is why it's a tad annoying that they start lower case.) Furthermore the same label name must appear in both terms and types -- in fact labels occupy a namespace separate vs terms or types. So Trex is a long way from from generic record handling like: recAppend :: ( rho'\__x ) => Rec rho' -> Rec ( __x :: a) -> Rec ( __x :: a | rho') recAppend rho ( __x = x ) = ( __x = x | rho ) in which I've used double-underscore prefix to signify a label variable. This is intended to extend a record `rho` with a singleton record. If we try appending a record with more than one field, beware that field order is arbitrary, so this recAppend rho ( __x = x, __y = y) = ... has no principal type (a familiar difficulty). The programmer doesn't care which way round labels `__x, __y` bind, providing they're distinct, but the typing does care. Some sort of generic record extend/concatenate would be great. You might, looking at Trex syntax, think that `|` is it. This is valid: ( x = "x" | (y = 'y' | (z = 3.14))) ( x = "x", y = 'y', z = 3.14 ) -- equivalent I could put any record value in place of `(z = 3.14)`. But not in place of the `x = ..` or `y = ...`: `|` is not an operator, not commutative, not associative. Furthermore this is a place where parentheses make a difference, unlike usually in Haskell. So the following are not equivalent, indeed they're all invalid syntax ( (x = "x") | (y = 'y' | (z = 3.14))) -- x = ... has parens ( x = "x" | (y = 'y' | z = 3.14 )) -- z = ... doesn't have parens ( x = "x" | y = 'y' | (z = 3.14) ) -- z = ... in parens OK, but y = ... is not ( (x = "x") | (y = 'y') | (z = 3.14) ) -- no chance I'd like to write term `( rho1 | rho2 )` to concatenate two records. That's currently unrecognised syntax, so I think could be added as such. What would be its type/is it principal? ( rho1 | rho2 ) :: (rho1' \\ rho2') => Rec( rho1' | rho2' ) -- inventing more syntax in which constraint `(rho1' \\ rho2')` requires the two rows' labels be mutually disjoint -- read "lacks all". Ur/web has something like this. Note I'm not envisaging `|` as a genuine operator: this is still hard-wired syntax; pipe is a reserved symbol in H98 anyway. AntC --000000000000043941057984a48c Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable <div dir=3D"auto">musing on this</div><div dir=3D"auto"><div><a href=3D"htt= ps://mail.haskell.org/pipermail/beginners/2018-October/018363.html">https:/= /mail.haskell.org/pipermail/beginners/2018-October/018363.html</a></div><br= ></div><div dir=3D"auto">to which my reply talked mainly about Trex.</div><= div dir=3D"auto"><br></div><div dir=3D"auto">Does it make sense to regard a= Trex record (or any free-standing anonymous/extensible record) as a contai= ner, with values 'lifted' into the structure, and an interface give= n by the label names, _not_ by order of fields? That is, "container&qu= ot; in the Monad sense?</div><div dir=3D"auto"><br></div><div dir=3D"auto">= Does it further make sense if all the values are already lifted into some F= unctor (like Maybe or (Either e) ) to hoist the Functor out over the record= structure?</div><div dir=3D"auto"><br></div><div dir=3D"auto">I'm not = sure I'm up with the program for Functor/Applicative/Monad everywhere. = If a record structure is a Functor, what's the type constructor? `Rec`?= If record structure is an Applicative, what's the constructor for `pur= e` that lifts into `Rec`? The `<*>` aka `ap` or bind for a Monad pres= umably is to take two records (with disjoint labels) and concatenate them t= o a single record.</div><div dir=3D"auto"><br></div><div dir=3D"auto">Given= </div><div dir=3D"auto"><br></div><div dir=3D"auto">data Person =3D Person = (Rec( name :: String, age :: Int))</div><div dir=3D"auto"><br></div><div di= r=3D"auto">we want, corresponding to the question:</div><div dir=3D"auto"><= br></div><div dir=3D"auto">Person <?$> =C2=A0(age =3D 27) <*> (= name =3D "Joe")</div><div dir=3D"auto"><br></div><div dir=3D"auto= ">in which operator <?$> is map-like: it takes a function to its left= that expects a record of n fields, then maps over n singleton records with= corresponding labels appearing in any order.</div><div dir=3D"auto"><br></= div><div dir=3D"auto">I might be able to get there by generics/reflection o= ver the field names embedded in data constructor `Person`s type, then overl= oading <?$> to look for each label. That is, if Hugs had any sort of = generics/TypeRep.</div><div dir=3D"auto"><br></div><div dir=3D"auto">Label = names in Trex are literals; there's no such thing as a label variable. = (Which is why it's a tad annoying that they start lower case.) Furtherm= ore the same label name must appear in both terms and types -- in fact labe= ls occupy a namespace separate vs terms or types. So Trex is a long way fro= m from generic record handling like:</div><div dir=3D"auto"><br></div><div = dir=3D"auto">recAppend :: ( rho'\__x ) =3D> Rec rho' -> Rec (= __x :: a) -> Rec ( __x :: a | rho')</div><div dir=3D"auto">recAppen= d rho ( __x =3D x ) =3D ( __x =3D x | rho )</div><div dir=3D"auto"><br></di= v><div dir=3D"auto">in which I've used double-underscore prefix to sign= ify a label variable. This is intended to extend a record `rho` with a sing= leton record. If we try appending a record with more than one field, beware= that field order is arbitrary, so this</div><div dir=3D"auto"><br></div><d= iv dir=3D"auto">recAppend rho ( __x =3D x, __y =3D y) =3D ...</div><div dir= =3D"auto"><br></div><div dir=3D"auto">has no principal type (a familiar dif= ficulty). The programmer doesn't care which way round labels `__x, __y`= bind, providing they're distinct, but the typing does care.</div><div = dir=3D"auto"><br></div><div dir=3D"auto">Some sort of generic record extend= /concatenate would be great. You might, looking at Trex syntax, think that = `|` is it. This is valid:</div><div dir=3D"auto"><br></div><div dir=3D"auto= ">( x =3D "x" | (y =3D 'y' | (z =3D 3.14)))</div><div dir= =3D"auto">( x =3D "x", y =3D 'y', z =3D 3.14 ) =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 -- equivalent</div><div dir=3D"auto"><br></div><di= v dir=3D"auto">I could put any record value in place of `(z =3D 3.14)`. But= not in place of the `x =3D ..` or `y =3D ...`: `|` is not an operator, not= commutative, not associative. Furthermore this is a place where parenthese= s make a difference, unlike usually in Haskell. So the following are not eq= uivalent, indeed they're all invalid syntax</div><div dir=3D"auto"><br>= </div><div dir=3D"auto">( (x =3D "x") | (y =3D 'y' | (z = =3D 3.14))) =C2=A0 =C2=A0 =C2=A0 -- x =3D ... has parens</div><div dir=3D"a= uto">( x =3D "x" =C2=A0| (y =3D 'y' | z =3D 3.14 )) =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0-- z =3D ... doesn't have parens</div><d= iv dir=3D"auto">( x =3D "x" | y =3D 'y' | (z =3D 3.14) ) = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 -- z =3D ... in parens OK, but y =3D ...= is not</div><div dir=3D"auto">( (x =3D "x") | (y =3D 'y'= ) | (z =3D 3.14) ) =C2=A0 =C2=A0 =C2=A0-- no chance<br></div><div dir=3D"au= to"><br></div><div dir=3D"auto">I'd like to write term `( rho1 | rho2 )= ` to concatenate two records. That's currently unrecognised syntax, so = I think could be added as such. What would be its type/is it principal?</di= v><div dir=3D"auto"><br></div><div dir=3D"auto">( rho1 | rho2 ) :: (rho1= 9; \\ rho2') =3D> Rec( rho1' | rho2' ) =C2=A0 =C2=A0-- inven= ting more syntax</div><div dir=3D"auto"><br></div><div dir=3D"auto">in whic= h constraint `(rho1' \\ rho2')` requires the two rows' labels b= e mutually disjoint -- read "lacks all". Ur/web has something lik= e this. Note I'm not envisaging `|` as a genuine operator: this is stil= l hard-wired syntax; pipe is a reserved symbol in H98 anyway.</div><div dir= =3D"auto"><br></div><div dir=3D"auto"><br></div><div dir=3D"auto">AntC</div= > --000000000000043941057984a48c-- --===============8518028055001502429== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Disposition: inline X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KSHVncy1Vc2Vy cyBtYWlsaW5nIGxpc3QKSHVncy1Vc2Vyc0BoYXNrZWxsLm9yZwpodHRwOi8vbWFpbC5oYXNrZWxs Lm9yZy9jZ2ktYmluL21haWxtYW4vbGlzdGluZm8vaHVncy11c2Vycwo= --===============8518028055001502429==--