Alex Rivera | Logout

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

Asked 2011-07-13T11:00:49.887
9

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.

Edit
Report

2 Answers

6

I like @hammar's answer, and you should also check out this article which provides another example.

But, you might want to think differently about your types. The boxing of Nameable into the SomeNameable data type usually makes me start thinking about whether a union type for the specific case is meaningful.

data Entity = H Human | B Building
instance Nameable Entity where ...

items = [H (Human "Don"), B (Building "Town Hall")]
answered 2011-07-13T12:36:33.480
3

You can try to use Existentially Quanitified types and do it like this:

data T = forall a. Nameable a => MkT a
items = [MkT (Human "bla"), MkT (Building "bla")]
answered 2011-07-13T11:53:31.777

Your Answer