I agree with David that using explicit `coerce`s can be quite verbose and may need ScopedTypeVariables and InstanceSigs. But visible type application should always work, because class methods always have a fixed type argument order. Regardless, requiring users to do all this for GND on Monad would be frustrating.
Actually, I just had an insight about this: there is no reason to use one deriving strategy for all methods in an instance. I can think of 4 ways to fill in the implementation of a class method in an instance:
1. Explicit, hand-written implementation
2. Defaulting to the implementation written in the class (or `error "undefined method"` in the absence of a default. This is essentially the default default.)
3. Stock implementation provided by GHC
4. Coerce
Ways 2, 3, and 4 all have extra restrictions: Way 2 might have extra type constraints due to a `default` signature. Way 3 restricts the choice of class and type. Way 4 works only on newtypes and then imposes role restrictions on the method's type.
GHC provides a `deriving` mechanism so that you can request Way 2 (`default`), 3 (`stock`), or 4 (`newtype`) to fill in every method in a class. But there's no need to provide this feature at such a course granularity. What about:
newtype N a = MkN (Foo a)
instance Blah a => C (N a) where
meth1 = ...
deriving default meth2 -- a bit silly really, as you can just leave meth2 out
deriving stock meth3 -- also silly, as C isn't a stock class, but you get the idea
deriving newtype meth4
We could also imagine
deriving newtype instance Blah a => Monad (N a) where
deriving default join -- not so silly anymore!
This syntax allows a `where` clause on standalone deriving allowing you to override the overall `deriving` behavior on a per-method basis.
I actually quite like this extension...
Richard
You *can* do this, but it's often not so concise. When the type constructor has parameters, you need to pin them down using ScopedTypeVariables. So you end up needing to give a signature for the method type in order to bring into scope variables you then use in the argument to coerce. If you have
newtype Foo f a = Foo (Foo f a)
then you may need
instance Bar f => Bar (Foo f) where
bah = coerce (bah @ f @ a)
:: forall a . C a => ...
to pin down the C instance.
If you don't want to use explicit type application (e.g., you're using a library that does not claim to have stable type argument order), things get even more verbose.
_______________________________________________
Glasgow-haskell-users mailing list
Glasgow-haskell-users@haskell.orghttp://mail.haskell.org/cgi-bin/mailman/listinfo/glasgow-haskell-users