0

(Related to Using custom instance when deriving an instance via GeneralizedNewtypeDeriving). Suppose that I have :

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main (main) where

data Cat = Cat String deriving (Show)
newtype Dolphin = Dolphin Cat deriving (Info,Show)

class Info a where
  info :: a -> (a,String)

instance Info Cat where
  info a = (a,show a)

main = do
  print $ info (Cat "mammal")
  --OK
  --OUTPUT 1 : (Cat "mammal","Cat \"mammal\"")

  print $ info (Dolphin $ Cat "mammal")
  --NOT OK
  --OUTPUT 2 : (Dolphin (Cat "mammal"),"Cat \"mammal\"")

  print $ show (Dolphin $ Cat "mammal")
  --OUTPUT 3 : "Dolphin (Cat \"mammal\")"

In output 2, we know that info is operating on an argument of type Dolphin (see the first element of the tuple), and yet it prints out "Cat \"mammal\"" for the second element of the tuple. Yet we know that show executed on an argument of type Dolphin should print out "Dolphin (Cat \"mammal\")" (see output 3). So why does ghci (in output 2) print out "(Dolphin (Cat "mammal"),"Cat \"mammal\"")" when I think it should print out "(Dolphin (Cat "mammal"),"Dolphin (Cat \"mammal\")")"?

Community
  • 1
  • 1
artella
  • 5,068
  • 4
  • 27
  • 35
  • 2
    Perhaps you were expecting the derived instance to copy the source code from the other instance, to produce `instance Info Dolphin where info a = (a, show a)`. While the types happen to work out in this case, they might not in general: imagine writing `showCat :: Cat -> String; showCat = show; instance Info Cat where info a = (a, showCat a)`. Now when you copy this source code to the new instance `instance Info Dolphin where info a = (a, showCat a)` you are trying to call `showCat` on a `Dolphin`! So instead newtype deriving must unwrap argument values and rewrap result values. – Daniel Wagner Dec 20 '14 at 08:34
  • @DanielWagner : that's a very good explanation. Makes complete sense now. – artella Dec 20 '14 at 12:32

1 Answers1

3

The only instance for Info with any code is the one for Cat, so the only possible value of the second element of the tuple is the result of show (a :: Cat) which is invariably "Cat \"...\"". The Info instance for Dolphin came from GeneralizedNewtypeDeriving; it does exactly what the Info instance for Cat did.

The Show instance for Dolphin did not come from GeneralizedNewtypeDeriving, it came from the ordinary mechanism for deriving (Show). It shows Dolphin as you expect. In the second example show (a :: Dolphin) is being used by the Show instance for (Show a, Show b) => Show (a, b) used by print to display a tuple of type (Dolphin, String).

The confusing bit here is that the Info instance for Cat is returning a tuple of type (Cat, String). The only thing GeneralizedNewtypeDeriving is doing is replacing Cat with Dolphin everywhere the type variable appeared in the Info instance. This gives an info :: Dolphin -> (Dolphin, String) that does exactly what info :: Cat -> (Cat, String) did.

Cirdec
  • 24,019
  • 2
  • 50
  • 100