Anonymous records. A solution to the problems of record-system.

Anonymous records. A solution to the problems of record-system. The current record system is notorious for three major flaws: 1. It does not solve the namespacing problem. I.e., you cannot have two records sharing field names in a single module. E.g., the following won't compile: data A = A { field :: String }data B = B { field :: String } 2. It's partial. The following code will result in a runtime error: data A = A1 { field1 :: String } | A2 { field2 :: String } main = print $ field1 $ A2 "abc" 3. It does not allow you to use the same field name for different types across constructors: data A = A1 { field :: String } | A2 { field :: Int } This proposal approaches all the problems above and also a fourth one, which is unrelated to the current record system: it allows one to avoid declaration of intermediate types (see details below). Gentlemen, I want you to meet, <#anonymous-records>Anonymous Records When a record-syntax is used in Haskell it's almost always a single-constructor ADT. A question rises then: why use ADT when you don't need its main feature (i.e., the multiple constructors)? This main feature is actually the root of the second and the third problem of record-syntax from the list above. In such situations one doesn't actually need ADT, but something more like a tuple with ability to access its items by name. E.g.: f :: (a :: Int, b :: String) -> Stringf rec = rec.b ++ show rec.a application: f (a = 123, b = "abc") So now you think "Okay, but how about naming it?". Well, not a problem at all - use the existingtype-construct: type TheRecord = (a :: Int, b :: String) Now, about the avoidance of intermediate types: type Person = (name :: String, phone :: (country :: Int, area :: Int, number :: Int)) See? No need to declare separate types for inner values. But, of course, if you need, you still can: type Phone = (country :: Int, area :: Int, number :: Int)type Person = (name :: String, phone :: Phone) We can nicely access the deeply nested fields, e.g.: personCountryCode :: Person -> IntpersonCountryCode person = person.phone.country Okay. What about the type ambiguity? E.g., in the following the Person is actually the same type asCompany: type Person = (name :: String, phone :: Phone)type Company = (name :: String, phone :: Phone) Easily solvable with a help of newtype: newtype Person = Person (name :: String, phone :: Phone)newtype Company = Company (name :: String, phone :: Phone) What about ADTs? Again, easy: data Product = Tea (brand :: Company) | Milk (brand :: Company, fatness :: Float) Now, the beautiful fact: This solution does not conflict with any existing feature of Haskell! As the examples show, it easily fits into the language as an extension. It can peacefully coexist with the existing record system of ADTs. Hence a complete backwards compatibility with old codebase. There's also a potential for many other additional features. <#links>Links - Source of this proposal https://gist.github.com/nikita-volkov/6977841 .

So basically, TRex?
On 14 October 2013 18:13, Nikita Volkov
Anonymous records. A solution to the problems of record-system.
The current record system is notorious for three major flaws:
It does not solve the namespacing problem. I.e., you cannot have two records sharing field names in a single module. E.g., the following won't compile:
data A = A { field :: String } data B = B { field :: String }
It's partial. The following code will result in a runtime error:
data A = A1 { field1 :: String } | A2 { field2 :: String }
main = print $ field1 $ A2 "abc"
It does not allow you to use the same field name for different types across constructors:
data A = A1 { field :: String } | A2 { field :: Int }
This proposal approaches all the problems above and also a fourth one, which is unrelated to the current record system: it allows one to avoid declaration of intermediate types (see details below).
Gentlemen, I want you to meet,
Anonymous Records
When a record-syntax is used in Haskell it's almost always a single-constructor ADT. A question rises then: why use ADT when you don't need its main feature (i.e., the multiple constructors)? This main feature is actually the root of the second and the third problem of record-syntax from the list above. In such situations one doesn't actually need ADT, but something more like a tuple with ability to access its items by name. E.g.:
f :: (a :: Int, b :: String) -> String f rec = rec.b ++ show rec.a
application:
f (a = 123, b = "abc")
So now you think "Okay, but how about naming it?". Well, not a problem at all - use the existingtype-construct:
type TheRecord = (a :: Int, b :: String)
Now, about the avoidance of intermediate types:
type Person = (name :: String, phone :: (country :: Int, area :: Int, number :: Int))
See? No need to declare separate types for inner values. But, of course, if you need, you still can:
type Phone = (country :: Int, area :: Int, number :: Int) type Person = (name :: String, phone :: Phone)
We can nicely access the deeply nested fields, e.g.:
personCountryCode :: Person -> Int personCountryCode person = person.phone.country
Okay. What about the type ambiguity? E.g., in the following the Person is actually the same type asCompany:
type Person = (name :: String, phone :: Phone) type Company = (name :: String, phone :: Phone)
Easily solvable with a help of newtype:
newtype Person = Person (name :: String, phone :: Phone) newtype Company = Company (name :: String, phone :: Phone)
What about ADTs? Again, easy:
data Product = Tea (brand :: Company) | Milk (brand :: Company, fatness :: Float)
Now, the beautiful fact:
This solution does not conflict with any existing feature of Haskell! As the examples show, it easily fits into the language as an extension. It can peacefully coexist with the existing record system of ADTs. Hence a complete backwards compatibility with old codebase. There's also a potential for many other additional features.
Links
Source of this proposal.
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

Yes, TRex indeed looks very similar. I'm quite surprised how I managed to
come up with something that much similar without knowing anything about
TRex.
So do you know anything about why it hasn't been yet implemented?
2013/10/14 Christopher Done
So basically, TRex?
Anonymous records. A solution to the problems of record-system.
The current record system is notorious for three major flaws:
It does not solve the namespacing problem. I.e., you cannot have two records sharing field names in a single module. E.g., the following won't compile:
data A = A { field :: String } data B = B { field :: String }
It's partial. The following code will result in a runtime error:
data A = A1 { field1 :: String } | A2 { field2 :: String }
main = print $ field1 $ A2 "abc"
It does not allow you to use the same field name for different types across constructors:
data A = A1 { field :: String } | A2 { field :: Int }
This proposal approaches all the problems above and also a fourth one, which is unrelated to the current record system: it allows one to avoid declaration of intermediate types (see details below).
Gentlemen, I want you to meet,
Anonymous Records
When a record-syntax is used in Haskell it's almost always a single-constructor ADT. A question rises then: why use ADT when you don't need its main feature (i.e., the multiple constructors)? This main feature is actually the root of the second and the third problem of record-syntax from the list above. In such situations one doesn't actually need ADT, but something more like a tuple with ability to access its items by name. E.g.:
f :: (a :: Int, b :: String) -> String f rec = rec.b ++ show rec.a
application:
f (a = 123, b = "abc")
So now you think "Okay, but how about naming it?". Well, not a problem at all - use the existingtype-construct:
type TheRecord = (a :: Int, b :: String)
Now, about the avoidance of intermediate types:
type Person = (name :: String, phone :: (country :: Int, area :: Int, number :: Int))
See? No need to declare separate types for inner values. But, of course, if you need, you still can:
type Phone = (country :: Int, area :: Int, number :: Int) type Person = (name :: String, phone :: Phone)
We can nicely access the deeply nested fields, e.g.:
personCountryCode :: Person -> Int personCountryCode person = person.phone.country
Okay. What about the type ambiguity? E.g., in the following the Person is actually the same type asCompany:
type Person = (name :: String, phone :: Phone) type Company = (name :: String, phone :: Phone)
Easily solvable with a help of newtype:
newtype Person = Person (name :: String, phone :: Phone) newtype Company = Company (name :: String, phone :: Phone)
What about ADTs? Again, easy:
data Product = Tea (brand :: Company) | Milk (brand :: Company, fatness :: Float)
Now, the beautiful fact:
This solution does not conflict with any existing feature of Haskell! As
On 14 October 2013 18:13, Nikita Volkov
wrote: the examples show, it easily fits into the language as an extension. It can peacefully coexist with the existing record system of ADTs. Hence a complete backwards compatibility with old codebase. There's also a potential for many other additional features.
Links
Source of this proposal.
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

This list is not for "Gentlemen" only. It's best not to make that
assumption with your language.
On Mon, Oct 14, 2013 at 5:13 PM, Nikita Volkov
Anonymous records. A solution to the problems of record-system.
The current record system is notorious for three major flaws:
1.
It does not solve the namespacing problem. I.e., you cannot have two records sharing field names in a single module. E.g., the following won't compile:
data A = A { field :: String }data B = B { field :: String }
2.
It's partial. The following code will result in a runtime error:
data A = A1 { field1 :: String } | A2 { field2 :: String } main = print $ field1 $ A2 "abc"
3.
It does not allow you to use the same field name for different types across constructors:
data A = A1 { field :: String } | A2 { field :: Int }
This proposal approaches all the problems above and also a fourth one, which is unrelated to the current record system: it allows one to avoid declaration of intermediate types (see details below).
Gentlemen, I want you to meet, <#141b7bed9546ffd9_anonymous-records>Anonymous Records
When a record-syntax is used in Haskell it's almost always a single-constructor ADT. A question rises then: why use ADT when you don't need its main feature (i.e., the multiple constructors)? This main feature is actually the root of the second and the third problem of record-syntax from the list above. In such situations one doesn't actually need ADT, but something more like a tuple with ability to access its items by name. E.g.:
f :: (a :: Int, b :: String) -> Stringf rec = rec.b ++ show rec.a
application:
f (a = 123, b = "abc")
So now you think "Okay, but how about naming it?". Well, not a problem at all - use the existingtype-construct:
type TheRecord = (a :: Int, b :: String)
Now, about the avoidance of intermediate types:
type Person = (name :: String, phone :: (country :: Int, area :: Int, number :: Int))
See? No need to declare separate types for inner values. But, of course, if you need, you still can:
type Phone = (country :: Int, area :: Int, number :: Int)type Person = (name :: String, phone :: Phone)
We can nicely access the deeply nested fields, e.g.:
personCountryCode :: Person -> IntpersonCountryCode person = person.phone.country
Okay. What about the type ambiguity? E.g., in the following the Person is actually the same type asCompany:
type Person = (name :: String, phone :: Phone)type Company = (name :: String, phone :: Phone)
Easily solvable with a help of newtype:
newtype Person = Person (name :: String, phone :: Phone)newtype Company = Company (name :: String, phone :: Phone)
What about ADTs? Again, easy:
data Product = Tea (brand :: Company) | Milk (brand :: Company, fatness :: Float)
Now, the beautiful fact:
This solution does not conflict with any existing feature of Haskell! As the examples show, it easily fits into the language as an extension. It can peacefully coexist with the existing record system of ADTs. Hence a complete backwards compatibility with old codebase. There's also a potential for many other additional features. <#141b7bed9546ffd9_links>Links
- Source of this proposalhttps://gist.github.com/nikita-volkov/6977841 .
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

Ramana Kumar
writes: This list is [not] for ...
This list _is_ for mature extensions to the language, to be considered for embedding into the formal standard [as I've been told several times]. So ...
On Mon, Oct 14, 2013 at 5:13 PM, Nikita Volkov wrote: ...
Nikita: do you have an implementation of your suggestion, or is it just a 'bright idea'? There have been many, many critiques of Haskell's current record system; and many, many proposals -- this is exactly what's blocking progress. I'm not sure you've fully characterised all the current difficulties. (For example: what is to be the principal type for your anonymous records? Are two records with the same-named fields, and at the same types, but in a different l-to-r sequence to be treated as 'equivalent'?) I don't see you comparing your suggestion to others. (As Christopher noted, it does at first sight look like TRex. Do you understand why TRex was abandoned?) There's a substantial body of recent debate and suggestions at http://ghc.haskell.org/trac/ghc/wiki/Records . This [Northern] Summer's GSoC project on OverloadedRecordFields that Wvv ref'd is just one point in the possible design space. You can trace backwards from those wiki pages to many threads of discussion.

Nikita: do you have an implementation of your suggestion, or is it just a 'bright idea'?
Just an idea for discussion with hope to interest enough people to draw in attention and probably get to implementation or at least to come up with collectively approved proposal.
I'm not sure you've fully characterised all the current difficulties. (For example: what is to be the principal type for your anonymous records? Are two records with the same-named fields, and at the same types, but in a different l-to-r sequence to be treated as 'equivalent'?)
I wanted to keep the introductory message as succinct as possible, so I eluded details like that. The types should be determined both by field names and their types, and since field names are by definition unique the order in which they appear shouldn't matter. E.g.: (a :: Int, b :: Char) is the same type as (b :: Char, a :: Int) and a different type from (z :: Char, x :: Int).
I don't see you comparing your suggestion to others. (As Christopher noted, it does at first sight look like TRex. Do you understand why TRex was abandoned?)
Christopher's notion was my first introduction to TRex, so I'm far from knowing much about it, but it in fact does look crazily similar to the current suggestion. From what I've read so far, it hasn't been abandoned, it just has only been implemented in Hugs.
There's a substantial body of recent debate and suggestions at http://ghc.haskell.org/trac/ghc/wiki/Records . This [Northern] Summer's GSoC project on OverloadedRecordFields that Wvv ref'd is just one point in the possible design space. You can trace backwards from those wiki pages to many threads of discussion.
Thanks! This page actually says that it only addresses namespacing and doesn't approach the first class record types problem, which this suggestion does approach. For this problem it refers to a more general page: http://ghc.haskell.org/trac/ghc/wiki/ExtensibleRecords - on which TRex is actually mentioned as having a high implementation cost.
From what I've seen OverloadedRecordFields looks very much like a workaround and it does not approach the first class record types problem. Very far from the elegance of TRex.

The reform to modernize record is already going on. See for more details here: http://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/Plan http://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/Implemen... -- View this message in context: http://haskell.1045720.n5.nabble.com/Anonymous-records-A-solution-to-the-pro... Sent from the Haskell - Haskell-prime mailing list archive at Nabble.com.

1) This system is week. It is still impossible write type Person = (lastName::Sting, name :: String) type Image = ( name :: ByteString, sizeX :: Int, sizeY :: Int ) name :: (b ~ Stirng) => (a,b) -> b name :: (a ~ ByteString) => (a,b,c) -> a 2) you already could write name :: (String, b) -> String name (a, _) = a You even could write now : data Person = Person Sting String data Image = Image ByteString Int Int class Rec_Name a where type OutRec_Name a name :: a -> OutRec_Name a instance (OutRec_Name Person ~ String) => Rec_Name Person type OutRec_Name Person = String name (Person _ a) = a instance (OutRec_Name Image ~ ByteString) => Rec_Name Image type OutRec_Name Image = ByteString name (Person a _ _) = a -- View this message in context: http://haskell.1045720.n5.nabble.com/Anonymous-records-A-solution-to-the-pro... Sent from the Haskell - Haskell-prime mailing list archive at Nabble.com.

It is still impossible write
type Person = (lastName::Sting, name :: String) type Image = ( name :: ByteString, sizeX :: Int, sizeY :: Int )
name :: (b ~ Stirng) => (a,b) -> b name :: (a ~ ByteString) => (a,b,c) -> a
I think, you misunderstood the primary part: in this proposal record fields are accessed not with functions, but more like fields in OO languages. So the problem of function sharing does not exist as such. E.g.: namesOfPersonAndImage :: Person -> Image -> String namesOfPersonAndImage person image = person.name ++ ", " ++ show image.name
participants (5)
-
AntC
-
Christopher Done
-
Nikita Volkov
-
Ramana Kumar
-
Wvv