Reputation: 9805
Why wouldn't GHC derive Applicative
for KO
?
#!/usr/bin/env stack
-- stack --resolver lts-17.10 script
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype KO a = KO a deriving (Functor, Applicative) -- doesn't derive Applicative
newtype OK f a = OK (f a) deriving (Functor, Applicative) -- that's ok
main :: IO ()
main = print "hello"
• Can't make a derived instance of ‘Applicative KO’ (even with cunning GeneralizedNewtypeDeriving): cannot eta-reduce the representation type enough • In the newtype declaration for ‘KO’typecheck
Upvotes: 2
Views: 376
Reputation: 152837
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 likenewtype
: when declaring a newtype
that wraps a type that already has an instance, reuse that instance, inserting and removing newtype wrappers at the appropriate momentsanyclass
: 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 momentsIn 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.
Upvotes: 5
Reputation: 9805
To illustrate how DerivingVia
is more explicit about each choice, here's a translation of each choice
#!/usr/bin/env stack
-- stack --resolver lts-17.10 script
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}
import Control.Monad.Identity
import Control.Monad.Trans.Maybe
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- Legacy
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- Endows with a Functor instance
newtype FStock a = FStock {unFStock :: a} deriving (Functor)
-- >>> unFStock $ (+ 1) <$> FStock 6
-- 7
-- Delegates the functor instance to f
newtype HFDel f a = HFDel {unHFDel :: f a} deriving (Functor)
-- >>> unHFDel $ (+1) <$> HFDel (Just 6)
-- Just 7
-- bad = unHFDel $ (+ 1) <$> HFDel 6 -- error nothing to delegate to
deriving instance Applicative f => Applicative (HFDel f)
-- >>> unHFDel $ (HFDel (Just (+1))) <*> HFDel (Just 6)
-- Just 7
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- Deriving Via
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
newtype F a = F {unF :: a}
deriving via Identity instance Functor F
deriving via Identity instance Applicative F
-- Endows with instances, by explicitely delegating to the Identity
-- >>> unF $ (+ 1) <$> F 6
-- 7
-- >>> unF $ F (+ 1) <*> F 6
-- 7
newtype G f a = G {unG :: f a}
-- Delegates explicitely the instances to f
deriving via (f :: * -> *) instance (Functor f) => Functor (G f)
deriving via (f :: * -> *) instance (Applicative f) => Applicative (G f)
-- >>> unG $ (+1) <$> G (Just 6)
-- Just 7
-- bad = unG $ (+1) <$> G 6 -- error nothing to delegate to
-- >>> unG $ G (Just (+1)) <*> G (Just 6)
-- Just 7
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
main :: IO ()
main = do
-- legacy
print $ unFStock $ (+ 1) <$> FStock 6 -- 7
print $ unHFDel $ (+ 1) <$> HFDel (Just 6) -- Just 7
--print $ unHFDel $ (+ 1) <$> HFDel 6 -- error nothing to delegate to
print $ unHFDel $ (HFDel (Just (+ 1))) <*> HFDel (Just 6) -- Just 7
-- deriving via
print $ unF $ (+ 1) <$> F 6 -- 7
print $ unF $ F (+ 1) <*> F 6 -- 7
print $ unG $ (+ 1) <$> G (Just 6) -- Just 7
-- print $ unG $ (+ 1) <$> G 6 -- error nothing to delegate to
print $ unG $ G (Just (+ 1)) <*> G (Just 6) -- Just 7
Upvotes: 0
Reputation: 9805
Additionally to the answer, here's some more detailed sample highlighting the two behaviors.
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- deriving Functor
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
newtype F a = F {unF :: a} deriving (Functor)
-- translates to
newtype F' a = F' a
deriving instance Functor F' -- F' endowed with instance -- (stock strategy)
-- whereas
newtype HF f a = HF {unHF :: f a} deriving (Functor)
-- delegates to
newtype HF' f a = HF' (f a)
deriving instance Functor f => Functor (HF' f) -- HF' f delegates to f
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- deriving Applicative
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- commented for typecheck
-- newtype G a = G a deriving (Functor, Applicative)
-- -- translates to pb
-- newtype G' a = G' a
-- deriving instance Functor G'
-- deriving instance Applicative G' -- G' not endowed with instance
-- -- whereas
newtype HG f a = HG (f a) deriving (Functor)
-- delegates
newtype HG' f a = HG' (f a)
deriving instance Functor f => Functor (HG' f) -- delegates to f
deriving instance Applicative f => Applicative (HG' f) -- delegates to f
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
---
-- Endows with a Functor instance
-- >>> unF $ (+ 1) <$> F 6
-- 7
-- Delegates the functor instance to f
-- >>> unHF $ (+1) <$> HF (Just 6)
-- Just 7
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Upvotes: 0