Hello!
Example: I wish to define a structured container type, let's call it a "RatsNest", that is type-variable in two ways:
(a) it is parameterized by a type of some class, let's call it "RatsTail", such that a RatsNest is a structure of things that have common properties. Certain operations between RatsNest values (e.g. a merge) are valid only if they are containers for the same kind of RatsTail. Such operations would not be different operations due to variation of thye contained type.
(b) it can be implemented in various ways; e.g. a simple version is an in-memory data structure, while another may be implemented in database storage. This corresponds to the Haskell idea of overloading, or classes.
Do you consider the standard design lacking? For example, -- sort of things to put into a RatNest class (Eq n) => RatTail n where is_the_same_rat:: n -> n -> Bool class (RatTail t) => RatNest c t where in_nest:: c t -> t -> Bool put:: c t -> t -> c t rat_fold:: (t -> z -> z) -> z -> (c t) -> z merge:: (RatNest c1 t) => c t -> c1 t -> c t merge c c1 = rat_fold put_perhaps c c1 where put_perhaps rat nest = if in_nest nest rat then nest else put nest rat instance RatTail Int where is_the_same_rat = (==) -- Pretend this is an in-memory implementation instance (RatTail t) => RatNest [] t where in_nest lst el = not $ null $ filter (is_the_same_rat el) lst put = (flip (:)) rat_fold = foldr -- Pretend this is a "disk" implementation -- At least it's unmarshalled. newtype Disk t = D String deriving Show instance (Read t, Show t, RatTail t) => RatNest Disk t where in_nest disk el = rat_fold (\item z -> z || is_the_same_rat item el) False disk put (D block) item = D $ '|' : ((show item) ++ block) rat_fold f z (D []) = z rat_fold f z (D ('|':block)) = f item $ rat_fold f z (D rest) where [(item,rest)] = readsPrec 1 block nest1 = [1,12,123] :: [Int] nest2 = D "" test1 = merge nest2 nest1 -- D "|1|12|123" test1' = merge test1 nest1 -- D "|1|12|123" test2 = merge [4,5,123] test1 -- [1,12,4,5,123] Haskell extensions are required, of course, because we use multi-parameter type classes.
At 18:42 18/03/2003 -0800, oleg@pobox.com wrote:
Example: I wish to define a structured container type, let's call it a "RatsNest", that is type-variable in two ways:
[...]
Do you consider the standard design lacking? For example,
I wasn't aware there was a standard design, but you have given me some pointers. Thanks! Studying your code was very interesting for me, as it has shown me something more of the kinds of idiom that work with Haskell. Concerning my question, I think the key part of your code is in: [[ class (RatTail t) => RatNest c t where ]] which I think is exactly what I was looking for, and is not a Haskell structure I've come across before. (I assume that this form does not indicate that RatNest... is a subclass of RatTail (as opposed to saying (RatTail t) => RatNest t); I certainly wouldn't want to have that.) I suppose this is the multi-parameter type class extension you mentioned. Can you say where this extension is described, and how widely implemented it is? Thank you for your response, I found it most helpful. #g ------------------- Graham Klyne <GK@NineByNine.org> PGP: 0FAA 69FF C083 000B A2E9 A131 01B9 1C7A DBCA CB5E
Hello!
I wasn't aware there was a standard design, Edison is a good standard (in particular, Collection.hs, found, for example, in /usr/local/share/hugs/lib/exts/). The following projects have a lot of helpful code:
http://sourceforge.net/projects/hbase/ http://sourceforge.net/projects/hfl/
[[ class (RatTail t) => RatNest c t where ]]
which I think is exactly what I was looking for, and is not a Haskell structure I've come across before. (I assume that this form does not indicate that RatNest... is a subclass of RatTail (as opposed to saying (RatTail t) => RatNest t); I certainly wouldn't want to have that.)
Indeed,
class (RatTail t) => RatNest c t where
says that collections of a class RatNest have elements of a class RatTail t: but collections themselves are not in the class RatTail. Using the OOP slang (which I hate), RatNest has-a RatTail but RatNest is-not-a RatTail. The method is_the_same_rat applies, in general, only to the elements of a RatNest, but not to the nest itself. Nothing prevents us from declaring a particular instance of a RatNest to be an instance of a RatTail as well. We can then store those instances in a RatNest. Nothing prevents us from making any RatNest a RatTail, so we can always store nests inside nests. -- sort of things to put into a RatNest -- Unlike the code in the previous message, -- this class declaration does not have the Eq context class RatTail n where is_the_same_rat:: n -> n -> Bool class (RatTail t) => RatNest c t where in_nest:: c t -> t -> Bool put:: c t -> t -> c t rat_fold:: (t -> z -> z) -> z -> (c t) -> z merge:: (RatNest c1 t) => c t -> c1 t -> c t merge c c1 = rat_fold put_perhaps c c1 where put_perhaps rat nest = if in_nest nest rat then nest else put nest rat instance RatTail Int where is_the_same_rat = (==) -- Pretend this is an in-memory implementation instance (RatTail t) => RatNest [] t where in_nest lst el = not $ null $ filter (is_the_same_rat el) lst put = (flip (:)) rat_fold = foldr -- Pretend this is a "disk" implementation -- At least it's unmarshalled. newtype Disk t = D String deriving Show instance (Read t, Show t, RatTail t) => RatNest Disk t where in_nest disk el = rat_fold (\item z -> z || is_the_same_rat item el) False disk put (D block) item = D $ '|' : ((show item) ++ block) rat_fold f z (D []) = z rat_fold f z (D ('|':block)) = f item $ rat_fold f z (D rest) where [(item,rest)] = readsPrec 1 block nest1 = [1,12,123] :: [Int] nest2 = D "" test1 = merge nest2 nest1 -- D "|1|12|123" test1' = merge test1 nest1 -- D "|1|12|123" test2 = merge [4,5,123] test1 -- [1,12,4,5,123] -- Every RatNest is a RatTail instance (RatTail t, RatNest c t) => RatTail (c t) where is_the_same_rat c1 c2 = subnest c1 c2 && subnest c2 c1 where subnest c1 c2 = rat_fold (\item res -> in_nest c1 item && res) True c2 -- Now we can make nests of nests test3 = merge [nest1] [test2] -- [[1,12,4,5,123],[1,12,123]] test3' = merge [nest1] [nest1] -- [[1,12,123]] test4 = put nest2 nest1 -- "|[1,12,123]" test5 = merge test4 (put test4 test2) -- D "|[1,12,4,5,123]|[1,12,123]" test7 = put [] test5 -- [D "|[1,12,4,5,123]|[1,12,123]"] The test7 shows three nested nests.
I suppose this is the multi-parameter type class extension you mentioned. Can you say where this extension is described, and how widely implemented it is?
GHC (with the flag -fglasgow-exts) and Hugs (with the flag -98) implement it. I think NHC does too.
At 11:41 20/03/2003 -0800, you wrote:
I suppose this is the multi-parameter type class extension you mentioned. Can you say where this extension is described, and how widely implemented it is?
GHC (with the flag -fglasgow-exts) and Hugs (with the flag -98) implement it. I think NHC does too.
Excellent! Your (previous) code example turned out to be extremely helpful. I was having a hard time figuring out what I needed to do to get my code to work in a reasonable fashion for my purposes, and it provided just the pointers I needed to break the logjam. Having it in a complete form with simple test cases was particularly helpful, and having got the type signatures sorted out I'm making much better progress on my project. Much appreciated. Having tried briefly to work without them, I now think multi-parameter classes are a really important extension for a range of programming tasks. Having support in Hugs and GHC seems to me like an important threshold of support. Thank you for your help. #g ------------------- Graham Klyne <GK@NineByNine.org> PGP: 0FAA 69FF C083 000B A2E9 A131 01B9 1C7A DBCA CB5E
participants (3)
-
Graham Klyne -
Graham Klyne -
oleg@pobox.com