Any pointers on how to make the closed type family example from the Haskell Wiki compile?
GHC/AdvancedOverlap (Solution 3)
Here is the version I tried:
{-# LANGUAGE
DataKinds,
FlexibleInstances,
MultiParamTypeClasses,
TypeFamilies,
UndecidableInstances #-}
data HTrue
data HFalse
type family ShowPred a where
ShowPred Int = HTrue
ShowPred Bool = HTrue
ShowPred [a] = ShowPred a
ShowPred a = HFalse
class Print a where
print :: a -> IO ()
instance (ShowPred a ~ flag, Print' flag a) => Print a where
print = print' (undefined :: flag)
class Print' flag a where
print' :: flag -> a -> IO ()
instance Show a => Print' HTrue a where
print' _ x = putStrLn (show x)
instance Print' flag a where
print' _ _ = putStrLn "No show method"
However, this still gives me:
Overlapping instances for Print' flag0 a.
(GHC 8.0.1 is the version I am using.)
Edit: Defining the second instance as Print' HFalse a
results in:
Could not deduce (Print' flag0 a) arising from a use of print' from the context: (ShowPred a ~ flag, Print' flag a).
Edit: Here is the example with all the corrections. All credit goes to @dfeuer.
{-# LANGUAGE
DataKinds,
FlexibleInstances,
MultiParamTypeClasses,
ScopedTypeVariables,
TypeFamilies,
UndecidableInstances #-}
import Prelude hiding (print)
import Data.Proxy
data HTrue
data HFalse
type family ShowPred a where
ShowPred Int = HTrue
ShowPred Bool = HTrue
ShowPred [a] = ShowPred a
ShowPred a = HFalse
class Print a where
print :: a -> IO ()
instance (ShowPred a ~ flag, Print' flag a) => Print a where
print = print' (Proxy :: Proxy flag)
class Print' flag a where
print' :: Proxy flag -> a -> IO ()
instance Show a => Print' HTrue a where
print' _ x = putStrLn (show x)
instance Print' HFalse a where
print' _ _ = putStrLn "No show method"
The initial error was caused by
instance Show a => Print' HTrue a where
print' _ x = putStrLn (show x)
instance Print' flag a where
print' _ _ = putStrLn "No show method"
These instances overlap when flag ~ HTrue
. The solution to this is to replace the second instance with
instance Print' HFalse a where ...
Your next problem was with
instance (ShowPred a ~ flag, Print' flag a) => Print a where
print = print' (undefined :: flag)
By default, the flag
variable in the instance context is not in scope in the instance body, so undefined :: flag
has an ambiguous type. The solution is to enable ScopedTypeVariables
.
My final note is that instead of making print'
take a value of type flag
and then passing it undefined
, you should probably use
print' :: proxy flag -> a -> IO ()
and then import Data.Proxy
to define
print = print' (Proxy :: Proxy flag)
The modern proxy-passing approach seems to be quite new, although less-satisfactory versions have been around for some time. See the answer to this question for some historical context.