mapping list of different types implementing the same function?

advertisements

I want to apply a function to every element in a list (map) but the elements may have different types but all implement the same function (here "putOut") like an interface. However I cannot create a list of this "interface" type (here "Outputable").

How do I map a list of different types implementing the same function?

import Control.Monad

main :: IO ()
main = do
 mapM_ putOut lst
 where
  lst :: [Outputable] -- ERROR: Class "Outputable" used as a type
  lst = [(Out1 1),(Out2 1 2)]

class Outputable a where
 putOut :: a -> IO ()

-- user defined:

data Out1 = Out1 Int deriving (Show)
data Out2 = Out2 Int Int deriving (Show)

instance Outputable Out1 where
 putOut out1 = putStrLn $ show out1

instance Outputable Out2 where
 putOut out2 = putStrLn $ show out2


Haskell doesn't allow for heterogenous lists. So you cannot make a list of Outputables, because your Out1 and Out2 are two distinct types, even if they both belong to the same type class.

But there is a workaround which allows to simulate heterogeneous lists with ExistentialQuantification. See an example of heterogeneous lists in Haskell wikibook.

How to use

  1. Put {-# LANGUAGE ExistentialQuantification #-} at the top of the module

  2. Define a box type, which hides heterogeneous elements inside:

      data ShowBox = forall s. Show s => SB s
      heteroList :: [ShowBox]
      heteroList = [SB (), SB 5, SB True]
    
    
  3. Define a necessary class instance for the box type itself:

      instance Show ShowBox where
        show (SB s) = show s
    
    
  4. Use a list of boxes.

An example

Your example may be rewritten as:

{-# LANGUAGE ExistentialQuantification #-}

main :: IO ()
main = do
 mapM_ print lst
 putStrLn "end"
 where
  lst :: [Printable]
  lst = [P (Out1 1),P (Out2 1 2)]

-- box type (2)
data Printable = forall a . Show a => P a

-- necessary Show instance for the box type (3)
instance Show Printable where show (P x) = show x

-- user defined:
data Out1 = Out1 Int deriving (Show)
data Out2 = Out2 Int Int deriving (Show)