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

Reply via email to