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 <nikita.y.volkov@gmail.com> wrote:

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

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


_______________________________________________
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime