How to define a class that allows uniform access to different records in Haskell?

Viewed 534

I have two records that both have a field I want to extract for display. How do I arrange things so they can be manipulated with the same functions? Since they have different fields (in this case firstName and buildingName) that are their name fields, they each need some "adapter" code to map firstName to name. Here is what I have so far:

class Nameable a where
  name :: a -> String

data Human = Human {
  firstName :: String
}

data Building = Building {
  buildingName :: String
}

instance Nameable Human where
  name x = firstName x

instance Nameable Building where
  -- I think the x is redundant here, i.e the following should work:
  -- name = buildingName
  name x = buildingName x

main :: IO ()
main = do
  putStr $ show (map name items)
  where
    items :: (Nameable a) => [a]
    items = [ Human{firstName = "Don"}
            -- Ideally I want the next line in the array too, but that gives an 
            -- obvious type error at the moment.
            --, Building{buildingName = "Empire State"}
            ]

This does not compile:

TypeTest.hs:23:14:
    Couldn't match expected type `a' against inferred type `Human'
      `a' is a rigid type variable bound by
          the type signature for `items' at TypeTest.hs:22:23
    In the expression: Human {firstName = "Don"}
    In the expression: [Human {firstName = "Don"}]
    In the definition of `items': items = [Human {firstName = "Don"}]

I would have expected the instance Nameable Human section would make this work. Can someone explain what I am doing wrong, and for bonus points what "concept" I am trying to get working, since I'm having trouble knowing what to search for.

This question feels similar, but I couldn't figure out the connection with my problem.

6 Answers
Related