Suppose that we have a typeclass class (A a, B a) => C a where
. Using newtype
will allow us to clone a data type and then automatically derive the instances via the GeneralizedNewtypeDeriving
language
extension (See how to write a derivable class? and Handling multiple types with the same internal representation and minimal boilerplate?).
QUESTION : Is it possible to get ghc to automatically derive A
and C
, but to use our own specified implementation of B
in deriving C
?
For example the following code (where A
= Planet
, B
= Lives
, C
= Description
) does not work as expected :
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
module Main (main) where
data Cat = Cat String
newtype Dolphin = Dolphin Cat deriving (Planet)
------------------------------------------------
class Planet a where
planet :: a -> String
class Lives a where
lives :: a -> String
class (Planet a, Lives a) => Description a where
description :: a -> String
------------------------------------------------
instance Planet Cat where
planet _ = "lives on planet earth,"
instance Lives Cat where
lives _ = "lives on land"
instance Description Cat where
description a = (planet a) ++ (lives a)
------------------------------------------------
instance Lives Dolphin where
lives _ = "lives in the sea"
--want the following derivation to use the instance of
--"Lives" for "Dolphin" above
deriving instance Description Dolphin
------------------------------------------------
main = do
print $ description (Cat "test")
-- > "lives on planet earth,lives on land"
-- OK
print $ description (Dolphin (Cat "test"))
-- > "lives on planet earth,lives on land"
-- NOT OK. Want "lives on planet earth,lives in the sea"
What I was expecting/wanted was for the Dolphin
instance of Lives
to be invoked in the derivation of Description
.
Obviously the following program works, but it requires one to explicitly instantiate Description
for Dolphin
:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
module Main (main) where
data Cat = Cat String
newtype Dolphin = Dolphin Cat deriving (Planet)
------------------------------------------------
class Planet a where
planet :: a -> String
class Lives a where
lives :: a -> String
class (Planet a, Lives a) => Description a where
description :: a -> String
------------------------------------------------
instance Planet Cat where
planet _ = "lives on planet earth,"
instance Lives Cat where
lives _ = "lives on land"
instance Description Cat where
description a = (planet a) ++ (lives a)
------------------------------------------------
instance Lives Dolphin where
lives _ = "lives in the sea"
instance Description Dolphin where
description a = (planet a) ++ (lives a)
------------------------------------------------
main = do
print $ description (Cat "test")
-- > "lives on planet earth,lives on land"
--[OK]
print $ description (Dolphin (Cat "test"))
-- > "lives on planet earth,lives in the sea"
--[OK]
p.s. What is puzzling is that if (in the first program) I do not declare :
instance Lives Dolphin where
lives _ = "lives in the sea"
Then ghc complains :
Main.hs:36:1:
No instance for (Lives Dolphin)
arising from the superclasses of an instance declaration
In the instance declaration for ‘Description Dolphin’
Seems strange that ghc would complain about the absence of instance Lives Dolphin where
if it is not using it in the (automatic) derivation of Description
for Dolphin
.
Consider the following:
newtype ProcessID = PID Int deriving Eq
What this does is write an instances that looks like
instance Eq PID where
(PID x) == (PID y) = x == y
In other words, when you call ==
on a PID
, it unwraps it into an ordinary Int
, and then executes ==
on that.
I imagine the deriving instance Description Dolphin
is doing exactly the same; unwrapping a Dolphine
into a Cat
, and then calling the description
method on that. Which isn't what you want at all!
Question: If the definition of description
is always the same, why does it need to be a class at all? Why can't you just define a regular function that does this?
(Or is this a simplification of some more complicated problem you want to solve?)