
Hello all! Let
data Book = Book { authors :: [String], title :: String, editor :: Maybe String, edition :: Maybe String, volume :: Maybe (Int, Int), -- e.g. volume 1 of 3 publisher :: String, year :: Int, pages :: Int }
and
convertBook :: Map String String -- a map from field names to values (string representation) -> Maybe Book
convertBook takes info about book from some external source (e.g. a BibTeX database) and returns Just book value or Nothing (if convertion failed). Fields of the Book datatype which are not (Maybe a) are required to be present. convertBook looks like
convertBook = (rq "title" (\b v -> b { title = v }) <.> rq "publisher" (\b v -> b { publisher = v }) <.> ... ) (Just $ Book [] "" Nothing Nothing Nothing "" 0 0)
I don't like the `(Just $ Book [] "" Nothing Nothing Nothing "" 0 0)' part, I would prefer instead someting like `empty :: Book'. So I define
class Empty e where empty :: e
But still I have to emplement instances by hand. There are a number of approaches to automatically derive instances (TH, generic classes in GHC, drift). What would you recommend using in this case? Or may be it would be better to drop out Empty and use something else? TIA -- WBR, Max Vasin.

On Wed, 17 Jan 2007 15:58:04 +0300
Max Vasin
Hello all!
Let
data Book = Book { authors :: [String], title :: String, editor :: Maybe String, edition :: Maybe String, volume :: Maybe (Int, Int), -- e.g. volume 1 of 3 publisher :: String, year :: Int, pages :: Int }
One suggestion: why not make the Book type more general, e.g. a list of labeled fields:
data Label = Author | Title | Editor | .... type Field = String newtype Book = Book [(Label,Field)]
The idea is that e.g. multiple authors would be represented by multiple entries with an "Author" label and optional fields can be omitted. Then the empty book is simply
empty = Book []
You might also find that this makes the processing of fields more uniform and easier to extend. Best regards, Pedro Vasconcelos.

"Pedro" == Pedro Baltazar Vasconcelos
writes:
Pedro> On Wed, 17 Jan 2007 15:58:04 +0300
Pedro> Max Vasin
Hello all!
Let
data Book = Book { > authors :: [String], > title :: String, editor :: Maybe String, > edition :: Maybe String, > volume :: Maybe (Int, Int), -- e.g. volume 1 of 3 > publisher :: String, > year :: Int, > pages :: Int }
One suggestion: why not make the Book type more general, e.g. a list of labeled fields:
data Label = Author | Title | Editor | .... type Field = String newtype Book = Book [(Label,Field)]
Pedro> The idea is that e.g. multiple authors would be represented Pedro> by multiple entries with an "Author" label and optional Pedro> fields can be omitted. Then the empty book is simply It does not enforce presence of required fields at type level. Also it does not enforce that fields pages and year are Int. And I want to move as much checks to compile time as possible. -- WBR, Max Vasin.

On Wed, 17 Jan 2007 16:57:54 +0300
Max Vasin
It does not enforce presence of required fields at type level. Also it does not enforce that fields pages and year are Int. And I want to move as much checks to compile time as possible.
You can combine the two solutions: a product-type of required fields plus list of optional fields. You're right that you lose the typing constraints on fields. I probabily woudn't bother, but you might be able to recover that with the type-classes or GADTs (?). But more important: it sounds to me like you have two conflicting requirements: one the one hand you want to have required fields (presumability those that were not Maybe types) but on the other hand you want an "empty" book (i.e. with "" for title, etc.). Regards, Pedro

"Pedro" == Pedro Baltazar Vasconcelos
writes:
Pedro> On Wed, 17 Jan 2007 16:57:54 +0300
Pedro> Max Vasin
It does not enforce presence of required fields at type level. Also it
does not enforce that fields pages and year are Int. And I want to move as much checks to compile time as possible.
Pedro> You can combine the two solutions: a product-type of Pedro> required fields plus list of optional fields. You're right Pedro> that you lose the typing constraints on fields. I probabily Pedro> woudn't bother, but you might be able to recover that with Pedro> the type-classes or GADTs (?). I have only a very basic understading of what GADTs are :-( Pedro> But more important: it sounds to me like you have two Pedro> conflicting requirements: one the one hand you want to have Pedro> required fields (presumability those that were not Maybe Pedro> types) but on the other hand you want an "empty" book Pedro> (i.e. with "" for title, etc.). I want to get as strict type checking as possible while writing as little code handling Book as possible. The empty value is needed only to start with when analising external represenation. And as suggested by Colin DeVilbiss and Henning Thielemann it will be better to use undefined for required fields. -- WBR, Max Vasin.

On 1/17/07, Max Vasin
Fields of the Book datatype which are not (Maybe a) are required to be present.
This doesn't actually answer your question, but if those fields are really required and you want to avoid the possibility of a "default" value sneaking into the program through a bug, you may prefer to use undefined instead of a non-bottom value for the required fields. That is, Book undefined undefined Nothing Nothing Nothing undefined undefined undefined (lots of typing, but if a bug slips in and you get a partially-defined book into the guts of the program, you'll die instead of silently using invalid data.)
class Empty e where empty :: e
But still I have to emplement instances by hand.
What would the strategy be here for automatically defining the instances? For example, what are the Empty instances for data Foo = Bar | Baz | Quux data Foo2 = Bar2 Int | Baz2 String | Quux2 (Maybe String)
Or may be it would be better to drop out Empty and use something else?
I see no inherent problem with Empty, just with the automated instantiation. Then again, I'd be tempted to wait to define Empty until I saw the second or third instance of its use. Colin

"Colin" == Colin DeVilbiss
writes:
Colin> On 1/17/07, Max Vasin
Fields of the Book datatype which are not (Maybe a) are required to be present.
Colin> This doesn't actually answer your question, but if those Colin> fields are really required and you want to avoid the Colin> possibility of a "default" value sneaking into the program Colin> through a bug, you may prefer to use undefined instead of a Colin> non-bottom value for the required fields. That is, Colin> Book undefined undefined Nothing Nothing Nothing undefined Colin> undefined undefined Hmm that's interesting... Colin> (lots of typing, but if a bug slips in and you get a Colin> partially-defined book into the guts of the program, you'll Colin> die instead of silently using invalid data.)
class Empty e where > empty :: e
But still I have to emplement instances by hand.
Colin> What would the strategy be here for automatically defining Colin> the instances? For example, what are the Empty instances Colin> for Colin> data Foo = Bar | Baz | Quux data Foo2 = Bar2 Int | Baz2 Colin> String | Quux2 (Maybe String) In my program (a BibTeX analog) Empty instances will be defined for datatypes representing bibliography entries which will have only one value constructor (records can't be used with newtype AFIAK). So for
data Foo = Foo t1 t2 ... tN
instance Empty Foo where empty = Foo (empty :: t1) (empty :: t2) ... (empty :: tN)
Or may be it would be better to drop out Empty and use something else? Colin> I see no inherent problem with Empty, just with the Colin> automated instantiation. Then again, I'd be tempted to Colin> wait to define Empty until I saw the second or third Colin> instance of its use.
There will be datatypes representing books, articles, thesis, reports, etc. -- WBR, Max Vasin.

On Wed, 17 Jan 2007, Max Vasin wrote:
Hello all!
Let
data Book = Book { authors :: [String], title :: String, editor :: Maybe String, edition :: Maybe String, volume :: Maybe (Int, Int), -- e.g. volume 1 of 3 publisher :: String, year :: Int, pages :: Int }
and
convertBook :: Map String String -- a map from field names to values (string representation) -> Maybe Book
convertBook takes info about book from some external source (e.g. a BibTeX database) and returns Just book value or Nothing (if convertion failed). Fields of the Book datatype which are not (Maybe a) are required to be present.
convertBook looks like
convertBook = (rq "title" (\b v -> b { title = v }) <.> rq "publisher" (\b v -> b { publisher = v }) <.> ... ) (Just $ Book [] "" Nothing Nothing Nothing "" 0 0)
I don't like the `(Just $ Book [] "" Nothing Nothing Nothing "" 0 0)' part, I would prefer instead someting like `empty :: Book'. So I define
class Empty e where empty :: e
But still I have to emplement instances by hand. There are a number of approaches to automatically derive instances (TH, generic classes in GHC, drift). What would you recommend using in this case? Or may be it would be better to drop out Empty and use something else?
Using a record with named fields is good style, as you pointed out later. Stick to it! Why do you need a type class? What about simply empty :: Book empty = Book [] "" Nothing Nothing Nothing "" 0 0 For updates empty { title = "new title" } is perfectly ok. You can also let undefined fields undefined. In Book {} all fields are undefined.

"Henning" == Henning Thielemann
writes:
Henning> On Wed, 17 Jan 2007, Max Vasin wrote:
Hello all!
Let
data Book = Book { > authors :: [String], > title :: String, editor :: Maybe String, > edition :: Maybe String, > volume :: Maybe (Int, Int), -- e.g. volume 1 of 3 > publisher :: String, > year :: Int, > pages :: Int }
and
convertBook :: Map String String -- a map from field names to values (string representation) > -> Maybe Book
convertBook takes info about book from some external source (e.g. a BibTeX database) and returns Just book value or Nothing (if convertion failed). Fields of the Book datatype which are not (Maybe a) are required to be present.
convertBook looks like
convertBook = (rq "title" (\b v -> b { title = v }) <.> > rq "publisher" (\b v -> b { publisher = v }) <.> > ... ) (Just $ Book [] "" Nothing Nothing Nothing "" 0 0)
I don't like the `(Just $ Book [] "" Nothing Nothing Nothing "" 0 0)' part, I would prefer instead someting like `empty :: Book'. So I define
class Empty e where > empty :: e
But still I have to emplement instances by hand. There are a number of approaches to automatically derive instances (TH, generic classes in GHC, drift). What would you recommend using in this case? Or may be it would be better to drop out Empty and use something else?
Henning> Using a record with named fields is good style, as you Henning> pointed out later. Stick to it! Why do you need a type Henning> class? What about simply Henning> empty :: Book empty = Book [] "" Nothing Nothing Nothing Henning> "" 0 0 Well the type class is not necessary, a TH function generating an empty value for given datatype. OTOH convertBook can be rewritten (using type class) as:
convertBook = (rq "title" (\b v -> b { title = v }) <.> rq "publisher" (\b v -> b { publisher = v }) <.> ... )
and it will be used like 'convertBook empty'. I like this way more since convertBook looks more declarative. Henning> For updates empty { title = "new title" } is perfectly Henning> ok. Henning> You can also let undefined fields undefined. In Henning> Book {} This will be enough if there were no optional fields. -- WBR, Max Vasin.
participants (4)
-
Colin DeVilbiss
-
Henning Thielemann
-
Max Vasin
-
Pedro Baltazar Vasconcelos