[Haskell-cafe] ANN: has-0.4 Entity based records

HASHIMOTO, Yusaku nonowarn at gmail.com
Tue May 4 12:18:29 EDT 2010


Hello,

I'm pleased to announce the release of my new library, named "has",
written to aim to ease pain at inconvinience of Haskell's build-in
records.

With the has, You can reuse accessors over records to write generic
function, combine records with another.

Repository is at GitHub: http://github.com/nonowarn/has
Uploaded on Hackage: http://hackage.haskell.org/package/has

So you can install this by "cabal install has"

You can use the has in three steps (without counting installation).

1. Write {-# OPTIONS_GHC -fglasgow-exts #-} top of your code,
   import Data.Has module.

> {-# OPTIONS_GHC -fglasgow-exts #-}
> import Data.Has

2. Define entities. "Entity" is data to index field in records.
   You can define an entity in one line.

> data Foo = Foo; type instance TypeOf Foo = Int

   (I lied) Before semicolon, declares entity. After semicolon,
   specifies the type to which the entity points.

   Define some entities for later examples.

> data Bar = Bar; type instance TypeOf Bar = Double
> data Baz = Baz; type instance TypeOf Baz = String
> data Quux = Quux; type instance TypeOf Quux = Bool

3. Define Records by concatinating fields of entities.

> type MyRecord = FieldOf Foo :&: FieldOf Bar :&: FieldOf Baz

   This is almost same as writing

< data MyRecord = MyRecord { foo :: Int
<                          , bar :: Double
<                          , baz :: String
<                          }

   To construct a value of record, remove colons and replace entities
   in record with values, and uncapitalize some words.

> aRecord :: MyRecord
> aRecord = fieldOf 42 & fieldOf 3.14 & fieldOf "string"

   And you can play with it.

To read/write/modify a value of field in records, you can use
functions with names stealed from data-accessor. But uses value-level
entities instead of accessors.

< Foo ^. aRecord           -- Reading
< Foo ^= 4649 $ aRecord    -- Writing
< Foo ^: (*2) $ aRecord    -- Modifying

If we have another record type contains Foo field, You can still
access the field in the same way.

> type AnotherRecord = FieldOf Bar :&: FieldOf Foo
> anotherRecord :: AnotherRecord
> anotherRecord = fieldOf 2.71 & fieldOf 31

< Foo ^. anotherRecord -- And this also works

Using these functions and Has constraint, You can write generic
functions over records.

> fooIsGreaterThan :: (Has Foo r) => r -> Int -> Bool
> fooIsGreaterThan r x = (Foo ^. r) > x

< aRecord `fooIsGreaterThan` 40       -- evaluated to True
< anotherRecord `fooIsGreaterThan` 40 -- evaluated To False

Even if you defined another record by combining records by (:&:), you
can still access the field, and apply to generic functions.

> type MoreRecord = FieldOf Baz :&: FieldOf Quux
> type CombinedRecord = AnotherRecord :&: MoreRecord
> combinedRecord :: CombinedRecord
> combinedRecord = (fieldOf 1.618 & fieldOf 39) & (fieldOf "sowaka" & fieldOf True)
>                    -- We can omit parentheses
>                    -- (even place parens anyware in record)

< combinedRecord `fooIsGreaterThan` 40 -- This yet works

The Has constraint provides not only genericity but also safety. If
the record doesn't satisfy the constraint, the type checker rejects
it.

> predicateOnRecords :: (Has Foo r, Has Quux r) => r -> Bool
> predicateOnRecords r = fooIsGreaterThan r 30 && (Quux ^. r)

< predicateOnRecords combinedRecord -- This is OK
< predicateOnRecords aRecord        -- This yields compile error

More examples included in package[1]

[1]: http://github.com/nonowarn/has/tree/master/examples/

This library is inspired by HList[2], and interfaces are stealed from
data-accessors[3]. And lenses[4], fclabels[5], and records[6] devote
themselves to similar purposes.

[2]: http://hackage.haskell.org/package/HList
[3]: http://hackage.haskell.org/package/data-accessor
[4]: http://hackage.haskell.org/package/lenses
[5]: http://hackage.haskell.org/package/fclabels
[6]: http://hackage.haskell.org/package/records

Enjoy!

-nwn


More information about the Haskell-Cafe mailing list