[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,
~wren
More information about the Haskell-Cafe
mailing list