peroni_santo
peroni_santo

Reputation: 367

Pattern matching on constructor wildcard?

If I have a constructor which is:

data Garage = Gar String

if I want to test whether a type is equal to my garage type I would do something like this:

(==(Gar _)) (Gar "g")

However, the compiler complains about the underscore. If I replace this with "g" it returns True. Is there a way so that I can compare with a wildcard?

Upvotes: 0

Views: 1526

Answers (4)

Satvik
Satvik

Reputation: 11208

A function like

isGarage (Gar _) = True
isGarage _       = False

is pretty useless as it has type Garage -> Bool. So it will always return True for Garage type and type check failure will occur for any other type.

I think patter matching would be sufficient for your requirement. But just to show that you can have such kind of function using type classes if you know the types you will be acting on. So possibly if you know you will be getting values from a given set of types then you can do something like

{-# LANGUAGE FlexibleInstances #-}
data Garage = Gar String

class IsGarage a where
    isGarage :: a -> Bool
    isGarage _ = False

instance IsGarage Garage where
    isGarage _ = True

instance IsGarage [Char]

In ghci

*Main> :t isGarage 
isGarage :: IsGarage a => a -> Bool
*Main> isGarage "3"
False
*Main> isGarage (Gar "2")
True

Upvotes: 0

AndrewC
AndrewC

Reputation: 32455

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 ()

Upvotes: 0

Matt Fenwick
Matt Fenwick

Reputation: 49085

Why doesn't your code work?

It looks like you want to pattern match, but actually you're calling the function == with arguments Gar _ and Gar "g". So Haskell gets confused and says something like "Pattern syntax in expression context: _".

How can it be fixed?

You can:

  1. add deriving Eq to the end of the data declaration, or

  2. implement Eq yourself:

    instance Eq Garage where
        (Gar l) == (Gar r) = l == r
    

Is it possible to pattern match against constructor wildcards? (for completeness)

Yes, here's a nonsense function:

f :: Garage -> Int
f (Gar "abc") = 12
f (Gar _) = 4

This would probably be more useful with a datatype that had multiple constructors, though.

Upvotes: 4

jdevelop
jdevelop

Reputation: 12296

you want to do pattern match, like this:

case x of
   Gar _ -> True
   _     -> False

if you want it as function, then add somsthing like

isGarage (Gar _) = True
isGarage _       = False

Upvotes: 2

Related Questions