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

Nikita Volkov nikita.y.volkov at gmail.com
Mon Oct 14 16:13:43 UTC 2013


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>
   .
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-prime/attachments/20131014/0d774382/attachment.html>


More information about the Haskell-prime mailing list