2
votes

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"
1
Try instance Print' HFalse a for your second instanceBenjamin Hodgson♦
Thanks for the suggestion, it sounds reasonable, but unfortunately results in another error (see edit).kloffy
@dfeuer Awesome, that works! If you post a very short explanation in an answer I would be happy to accept it.kloffy

1 Answers

1
votes

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.