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 _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe