[Haskell-cafe] generalized newtype deriving allows the definition of otherwise undefinable functions

wren ng thornton wren at freegeek.org
Wed Mar 10 18:37:18 EST 2010

Wolfgang Jeltsch wrote:
> Hello,
> some time ago, it was pointed out that generalized newtype deriving could be 
> used to circumvent module borders. Now, I found out that generalized newtype 
> deriving can even be used to define functions that would be impossible to define 
> otherwise. To me, this is surprising since I thought that generalized newtype 
> deriving was only intended to save the programmer from writing boilerplate 
> code, not to extend expressiveness.

Let's dig down and figure out the problem. When you annotate the type 
"Wrapped a" with "deriving (Iso a)" what are you saying? You're saying 
that the compiler should derive an instance (Iso a (Wrapped a)). Well, 
that instance gives you the method instance conv :: forall f. f a -> f 
(Wrapped a). The funny thing is that the only implementation for 
---something like--- that type would be fmap Wrap. But that 
implementation would introduce the requirement (Functor f), which is 
missing in the conv type. This is possible because of the representation 
model where a and (N a) have the same runtime representation, but it 
violates the intensional distinction between those types, by way of 
presuming functorality of any type of kind (k -> *). Yeah, we can't do 
that legally in the language, so the GeneralizedNewtypeDeriving 
implementation is buggy.

Regarding the use of GeneralizedNewtypeDeriving for implementing 
functions that are faster than otherwise possible, these particular 
derivations should not be done without the (Functor f) restriction in 
the type of the derived function. It doesn't matter that the 
implementation does not use the fmap implementation (assuming that 
implementation is lawful), it matters that the derived function cannot 
be written at all ---efficiently or otherwise--- without assuming such 
an fmap exists.

Special casing things like this so they require the (Functor f) 
restriction won't solve everything. I'm sure we could create a different 
example that violates a different class. The general solution would seem 
to be making sure that the newtype only occurs in "top-level" positions 
within the type of the derived functions. Where "top-level" means  that 
it is not embedded within an unknown type constructor, though we can 
legitimately bake in support for well-known type constructors like (->), 
(,), Either, Maybe, [],...

Live well,

More information about the Haskell-Cafe mailing list