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:
What this does is write an instances that looks like
In other words, when you call
==on aPID, it unwraps it into an ordinaryInt, and then executes==on that.I imagine the
deriving instance Description Dolphinis doing exactly the same; unwrapping aDolphineinto aCat, and then calling thedescriptionmethod on that. Which isn't what you want at all!Question: If the definition of
descriptionis 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?)