Generic deriving: new default methods
Simon Peyton-Jones
simonpj at microsoft.com
Fri Jan 7 10:05:38 CET 2011
Several thoughts
First, about type families:
deriving instance (Representable0 a rep, GenericEq rep) => Eq a where
eq = genericEqDefault
Didn't we decide to use type families, in the end ? So this would be
deriving instance (Representable0 a, GenericEq (Rep a)) => Eq a where
eq = genericEqDefault
where Rep is the type family?
Returning to your main qn. In our Derivable Type Classes work each method of a class could have
1. No default method, OR
2. A polymorphic default method (a la Haskell 98), OR
3. A generic default method (a la Derivable Type Classes)
e.g.
class C a where
op1 :: a -> a
op2 :: a -> a
op2 x = x
op3 :: a -> a
op3 {| p + a |} (Left x) = ...
... etc...
The methods op1, op2, op3 illustrate the three cases. Note that (a) the choice is made once for all in the class decl, and (b) it can be made independently for each method.
This decision, made in the class decl, specifies what to do when you have an instance declaration
instance ... => C (T a) where
...no method specified for 'op'...
If no method is specified in the instance, it is "filled in" in the way specified in the class decl. In the three cases:
1. No default => fill in with "error "missing method op1""
2. Polymorphic default => fill in with a call to the default function
3. DTC generic default => generate code according to the template
I think we should just do the same. Add a fourth choice (well, the third might disappear!) Something like
class C a where
op4 :: [a] -> a
deriving op4 = genericOp4Default -- Syntax?
Then, in an instance decl, if op4 is specified with a Pedro-deriving spec as above in the class decl, and the instance decl gives no defn for op4, then we fill in with the specified code.
Thoughts:
· The definition for op4 in the class decl should have form Cxt => [a] -> a, but there's no inherent reason that Cxt must be (Representable ... ). I suppose we could simply infer it. Then we could say, for example,
class C a where
op4 :: [a] -> a
deriving op4 = minimum :: Ord a => [a] -> a
instance Ord a => C (T a)
The instance would behave exactly as if you'd written
instance Ord a => C (T a) where
op4 = minimum
which would be fine if (T a) was an instance of Ord.
· I'm not sure about the syntax for specifying that op4 is a generic-deriving method. A pragma is not good, because the program is meant to make sense without them.
· Suppose that you wanted *all* the methods of an instance to use the generic deriving mechanism. Then you could write
instance Cxt => C (T a)
and all is fine. The *instance* doesn't have to say "deriving" stuff. But if C had generic-deriving methods, then Cxt might well need to include (Representable0 a) etc.
Simon
From: José Pedro Magalhães [mailto:jpm at cs.uu.nl]
Sent: 06 January 2011 15:55
To: Simon Peyton-Jones
Cc: atze; Johan Jeuring; Andres Löh; cvs-ghc at haskell.org
Subject: Generic deriving: new default methods
Hello Simon and all,
I am not sure what is the best way to proceed with the generic default methods. But first, some context: for the new generic deriving mechanism, being implemented in GHC [1] in a way similar to UHC [2], we need some form of default generic methods. Taking the Eq class and (==) as example:
class Eq a where
-- type signatures
eq, neq :: a -> a -> Bool
-- regular defaults
eq a b = not (neq a b)
neq a b = not (eq a b)
Our generic eq uses a class
class GenericEq f where
geq :: f a -> f a -> Bool
and a default method, which takes care of conversion from a user-defined datatype to its representation, and then applying the generic function [3]:
genericEqDefault :: (Representable0 a rep, GenericEq rep) => a -> a -> Bool
genericEqDefault a b = geq (from0 a) (from0 b)
To be able to derive generic equality for a user-defined datatype D, we need to generate an instance Eq D where eq is genericEqDefault. That is:
instance Eq D where
eq = genericEqDefault
So the compiler has to know that this genericEqDefault is somehow associated to the Eq class. In UHC we used a pragma:
{-# DERIVABLE Eq eq genericEqDefault #-}
That is, class Eq has a generic method eq with default implementation genericEqDefault.
For GHC, we first thought of something like:
class Eq a where
... -- all the stuff as before
deriving eq = genericEqDefault
That is, the generic default would stay together with the class declaration, a bit like regular defaults [4]. The problem here is that the "deriving eq" part doesn't really typecheck under the same context as the regular default: we need the context
(Representable0 a rep, GenericEq rep).
What is the best choice here? We could decide to accept the code above and implicitly use the context from genericEqDefault. I'm not sure on what kind of error messages this would result, though. Another alternative is to decouple the generic default, as in UHC. Proper syntax for the DERIVABLE pragma could be something like:
deriving instance (Representable0 a rep, GenericEq rep) => Eq a where
eq = genericEqDefault
I'm happy to hear your thoughts on this.
Cheers,
Pedro
[1] http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/GenericDeriving
[2] http://www.dreixel.net/research/pdf/gdmh_nocolor.pdf
[3] In UHC we needed to pass the representation explicitly as an argument to genericEqDefault. In GHC, if we use FunctionalDependencies or TypeFamilies, we can avoid this.
[4] Note that we must keep regular defaults as well, since users might want to use the Eq class without any generics.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/cvs-ghc/attachments/20110107/5c014c52/attachment-0001.htm>
More information about the Cvs-ghc
mailing list