Maybe your question assumes that you'll need to do run-time type checking in Haskell, but that's not necessary. Haskell will ensure all your types are correct at compile time, so a function which checks whether data is part of your Garage data type is not needed and wouldn't work:
justPrintGarages (Gar x) = print x -- if the argument is a Garage, print its content justPrintGarages _ = return () -- if it's anything else, do nothing.
If we ask ghci what type justPrintGarages has, it will tell us
printGarages :: Garage -> IO ()
Yikes! Our function that was supposed to tell us whether we had a garage only works on garages??? Yup. That's because Haskell deliberately stops you from mixing types up, because it's a quagmire of runtime errors. Static typing is your friend. Static typing takes away a world of pain. Because of static typing, you can't define
printGaragesAndShebangs (Gar x) = print x printGaragesAndShebangs ('#':'!':xs) = putStr xs printGaragesAndShebangs _ = return ()
You'll get a type error. You can't just treat Strings and Garages the same.
If you want to mix garages and strings, here's the way to do it, keeping type safety:
data GarageStringInt = GsiG Garage | GsiS String | GsiI Int
(GarageStringInt is a horrible name and so are GisG etc, but if you need this in your code it'll represent something sensible (I hope) and you can give it a name that describes what it represents.)
Now we can write
printGaragesAndShebangs :: GarageStringInt -> IO () printGaragesAndShebangs (GsiG (Gar x)) = print x printGaragesAndShebangs (GsiS ('#':'!':xs)) = putStr xs printGaragesAndShebangs _ = return ()