Heterogenous lists using type classes
Your original problem 


  that pattern matching wont work anymore if I decide to add any fields to the Human or Bot constructors later, so I would have to adjust this every time I change the constructors.


can be easily solved by matching data constructors without their fields:

nextMessage :: User -&gt; Message -&gt; IO Message
nextMessage Human{} msg = putStr ("New Message: " ++ msg ++ " \n&gt; ") &gt;&gt; getLine
nextMessage Bot{} msg = return $ msg + "is an interesting question!"


In this case changing fields in Human or Bot won't break nextMessage.

You can enable NamedFieldPuns extension to get only specified fields :

{-# LANGUAGE NamedFieldPuns #-}

nextMessage :: User -&gt; Message -&gt; IO Message
nextMessage Human{humanName} msg = putStr ("New Message: " ++ msg ++ " \n&gt; ") &gt;&gt; getLine
nextMessage Bot{botName} msg = return $ msg + " is an interesting question!"


Haskell also allows different datatypes to have common field names, so the code could look like this:

{-# LANGUAGE NamedFieldPuns #-}

data User = Human { name :: String, age :: Int }
          | Bot   { name :: String, age :: Int }
            deriving (Show, Eq)

nextMessage Human{name} msg = putStr ("New Message: " ++ msg ++ " \n&gt; ") &gt;&gt; getLine
nextMessage Bot{name} msg = return $ msg ++ " is an interesting question!"