[Hs-Generics] FW: [Haskell-cafe] SYB vs HList (again)

oleg at pobox.com oleg at pobox.com
Fri Mar 16 02:18:47 EDT 2007

[Please follow-up to generics at haskell.org]

S. Alexander Jacobson wrote:
> HLists require you to define Labels and basically only use label
> values that are themselves either scalar or HLists.
> ...
> With SYB you create field labels using newtype (or data) declarations
> e.g.
>    data Salary = S {salary::Float}
> With HList, label declarations are really verbose e.g.
>    data SalaryLabel deriving(Typeable)
>    type Salary = Field (Proxy SalaryLabel) Int
>    salary = proxy :: Proxy FooLabel

Actually there is no requirement that HList record names must be 
scalar `labels', must be Proxies and require such a complex
declaration. From HList's high point of view, any collection can be a
record provided the type of each item is unique and there is some way
to extract the value associated with that type. The HList library
provides two implementations of Records (and there was one more,
obsolete now). There could be more. For example, I have just committed
a yet another implementation,
Here a record is a list of things that have a type and a value and
provide a way to extract that value. The example from the end of this
file seems worth quoting:

> data Name      = Name String String deriving Show
> newtype Salary = S Float deriving Show
> data Dept      = D String Int deriving Show
> person = (Name "Joe" "Doe") .*. (S 1000) .*. (D "CIO" 123) .*. emptyRecord
> -- could be derived automatically, like Typeable...
> instance Fieldish Name (String,String) where 
>     fromField (Name s1 s2) = (s1,s2)
> instance Fieldish Salary Float where
>     fromField (S n) = n
> instance Fieldish Dept (String,Int) where
>     fromField (D s n) = (s,n)
> test1 = show person
> -- When a field acts as a label, only its type matters, not the contents
> test2 = person .!. (Name undefined undefined)
> test3 = person .!. (undefined::Salary)
> test5 = person .!. (D "xxx" 111)

> I don't know exactly how HList handles default values but I assume you
> can restrict use of those values to explicit deserialization contexts.
> Is that correct?

I'm not sure what you mean about the restriction of default values to
deserialization contexts. Anyway, HList provides a left-biased union
of two records: hLeftUnion r1 r2 is the record r1 augmented with all
the fields from r2 that didn't occur in r2. One may consider r2 to be
the record with default fields and the corresponding values.

> It would be really nice if there was some way to tell Haskell that
> HLists have no more fields than the ones you happen to be getting and
> setting in your code. Effectively that would mean you get data
> structure inference not just function type inference which would be
> really cool!

I'm not sure I follow. Could you outline an example of the code you
wish work? Incidentally, a lot of the library depends on the record
types being members of some specific classes. One can define

> newtype ClosedRecord = ClosedRecord r

To make a ClosedRecord to be a record from which we can extract the
values of some fields, we merely need to say
> instance HasField l r v => HasField l (ClosedRecord r) v
> where hLookupByLabel l (ClosedRecord r) v = hLookupByLabel l r v

Since we did not make this record the member of HExtend or HAppend, it
is not extensible.

More information about the Haskell-Cafe mailing list