GHC has four ways of creating instances:
stock
: write new implementations of each of the class's methods from scratch via pattern matching and the like
newtype
: when declaring a newtype
that wraps a type that already has an instance, reuse that instance, inserting and removing newtype wrappers at the appropriate moments
anyclass
: declare an instance with no method definitions (hence using default implementations for every method, if there are any)
via
: a generalization of newtype
, it lets you inherit the instance from any other type with visibly the same representation, again by inserting/removing newtype wrappers at appropriate moments
In your code snippet, the stock
method is used to derive Functor
because you have turned on DeriveFunctor
. There is currently no stock
derivation for Applicative
, though. The via
method only ever fires when explicitly requested, and you have not turned on DeriveAnyClass
, so the only option left is newtype
, so GHC attempts to inherit an instance from the wrapped type. Then it runs into trouble, because Applicative
is supposed to be for container types, and the contained type isn't one, so it complains.
This explains the Functor
vs Applicative
difference for your first snippet. For the first vs second snippet difference for Applicative
, we need only observe that after unwrapping the second one, we do have a container type; hence you get an Applicative
instance for OK
any time its f
argument has one.
See also the documentation on deriving strategies.
:i Applicative
you'll see this:type Applicative :: (* -> *) -> Constraint
- so a type can be applicative if it's kind is* -> *
(the container is applicative - like[]
orMaybe
- not[a]
,Maybe a
) - that's why it is not working - your newtype is a wrapper for any type - if you'd make an instance forOK
you would doApplicative f => instance Applicative (OK f)
(Ok f : * -> *
) - try to write an instance forKO
yourself - there is one (basicallyIdentity
- look it up) - but it's not what you would expect from a newtype wrapper – Random DevIdentity
behaviour is what you want, you could useDerivingVia
and saynewtype KO a = KO a deriving (Functor, Applicative) via Identity
– kosmikusderiving via
is more regular and less surprise – nicolas:i Functor
also showstype Functor :: (* -> *) -> Constraint
. Why doesn'tFunctor
pose the same problem, then? – EnlicoFunctor
doesn't pose the same problem because it is using a different instance generation method. My answer has some details and a link to the full documentation. – Daniel Wagner