9
votes

Newtypes are often used to change the behavior of certain types when used in certain class contexts. For example, one would use the Data.Monoid.All wrapper to change the behavior of Bool when used as a Monoid.

I'm currently writing such a newtype wrapper that would apply to a large range of different types. The wrapper is supposed to change the behavior of one specific class instance. It might look like this:

newtype Wrapper a = Wrapper a

instance Special a => Special (Wrapper a) where
  -- ...

However, adding this wrapper will often change the usability of the wrapped type. For example, if I previously was able to use the function mconcat :: Monoid a => [a] -> a, I am not able to now use it for a list of wrapped values.

I can of course use -XGeneralizedNewtypeDeriving and newtype Wrapper a = Wrapper a deriving (Monoid). However, this only solves the problem for Monoid and no other class, while I will be dealing with an open world full of different classes, and standalone orphan generalized newtype deriving is not really a practical option. Ideally, I'd like to write deriving hiding (Special) (deriving every class except Special), but that's not valid Haskell, of course.

Is there some way of doing this or am I just screwed and need to add a GHC feature request?

2
You're screwed, and also I very much doubt that this can even be added to GHC. How is it supposed to derive instances for classes that aren't even in scope?Daniel Wagner
Well, if type resolution demanded a Monoid (Wrapper a) instance, it would detect that Wrapper is a newtype, and also check whether it had the "implicit derivation" flag. If so, the instance for Monoid a is simply used. As I understand it, the instance resolution only has to happen once, as the functions called by a function with a declared class context simply blindly re-use the passed-in instance dictionaries of the parent function. There doesn't actually have to be a statically declared specialized instance value in a source file. It would be rather simple to implement, really.dflemstr
@DanielWagner Why should this be impossible? Just tell GHC it inherits whatever instances the underlying type does. I know GHC cannot currently do this, but presumably the purpose of extensions is to add new feature.Gabriella Gonzalez
@GabrielGonzalez that is essentially what GHC does now for newtypederiving...but it is not sound! This is because of a problem with type equalities (also, it screws with module boundaries). What newtype deriving SHOULD do is generate new code (at least for type checking) but this can't work in a fully "open" wayPhilip JF
Using "standalone orphan generalized newtype deriving" is pretty much the closest you're going to get, I'm afraid.C. A. McCann

2 Answers

3
votes

Look, GeneralizedNewtypeDeriving is unsafe. In that vein, here is an unsafe way of doing it

{-# LANGUAGE GADTs, ConstraintKinds #-}
import Data.Monoid
import Unsafe.Coerce

data Dict c where
  Dict :: c => Dict c

newtype Wrapper a = Wrapper a

addDictWrapper :: Dict (f a) -> Dict (f (Wrapper a))
addDictWrapper = unsafeCoerce

you can then use it anytime you need the typeclass instance

intWrapperNum :: Dict (Num (Wrapper Int))
intWrapperNum = addDictWrapper Dict

two :: Wrapper Int
two = case intWrapperNum of
           Dict -> 1 + 1

this system of passing around explicit dictionaries is highly general, and has a pretty good (although experimental) library to support it called Data.Constraint

0
votes

I'm afraid there is no direct way how to do that in GHC. But I think you could solve your problem using Template Haskell.