Ben, thanks for your clever questions. Let's look at a little GHC 6.4 demo.
{-# OPTIONS -fglasgow-exts #-} import Data.Generics main = do print $ toConstr () == toConstr () print $ toConstr (5::Int) == toConstr () print $ toConstr (\(x::Int) -> x) == toConstr () print $ id `shallowEq` (id::Int -> Int) shallowEq :: Data a => a -> a -> Bool shallowEq x y = toConstr x == toConstr y This gives: *Main> main True False *** Exception: toConstr Some observations: a) We need to say what sort of Num this 5 is because otherwise instance selection will not know how to obtain the constructor for 5. In this sense, SYB is not different from normal type-class-based programming. Well, modulo default declarations; http://www.haskell.org/tutorial/numbers.html So this works (because of a default declaration) *Main> show 5 == show () False This one doesn't because there is no default declaration: *Main> toConstr 5 == toConstr () <interactive>:1:0: Ambiguous type variable `a' in the constraints: ... b) You are right. An independently polymorhic shallowEq is more expressive since it allows us to compare the top-level constructors of different specializations of the same type, e.g., Just Char vs. Just String. shallowEq' :: (Data a, Data b) => a -> b -> Bool shallowEq' x y = toConstr x == toConstr y You also spotted the issue that shallowEq' cannot operate on data of types a and b where either of these is a type scheme. The reason for that is that the instances of Data (and Typeable) involve constraints for the types of the children. As long as these children types are not fixed, toConstr cannot be computed since instance selection cannot be completed. In general, this is a good idea because of the types of gfoldl (and the gmap derivates), but for exercises like shallow equality this is in the way. I don't have a good solution to offer. (I don't see either that this is a show stopper in practice.) We may think of splitting up the API as to define several more precise classes ... c) Just for the record, SYB's toConstr throws for function types. Ralf > -----Original Message----- > From: [EMAIL PROTECTED] [mailto:haskell-cafe- > [EMAIL PROTECTED] On Behalf Of Ben Lippmeier > Sent: Tuesday, July 19, 2005 8:56 PM > Cc: haskell-cafe@haskell.org > Subject: Re: [Haskell-cafe] Proposal: deriving ShallowEq? > > Ralf Lammel wrote: > > As Bulat points out, the GHC primitive dataToTag# > > indeed nicely solves the problem. Ben, just for > > completeness' sake; with SYB, you get such reflective > > information too (and others): > > > > shallowEq :: Data a => a -> a -> Bool > > shallowEq x y = toConstr x == toConstr y > > > > (dataToTag# returns Int, while toConstr comprises other things like the > > constructor name.) > > > > Ralf, > Yes, I ended up using the "propper" SYB approach instead, though I have > noticed that the reflection data types Constr and DataRep make no > mention of type variables or functions. > > For example, this works fine: > > getTag (Just 5) ==# getTag (Just{}) > > getTag (Just (\x -> x)) ==# getTag (Just{}) > > But this does not > > toConstr (Just 5) == toConstr (Just{}) > Ambiguous type variables. > > > toConstr (Just (\x -> x)) == toConstr (Just{}) > No instance for Data (t -> t) > > I appreciate the reasons why this is so, though I think it's interesting > to see the practical consequences. > > A toConstr version of shallowEq works ok so long as you provide a type > signature to constrain both arguments to be the same type, and one of > them is always fully constructed - which is fine for me at the moment. > > Ben. > > > _______________________________________________ > Haskell-Cafe mailing list > Haskell-Cafe@haskell.org > http://www.haskell.org/mailman/listinfo/haskell-cafe _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe