1
votes

An issue I sometimes encounter when programming in Haskell is sometimes I want to match a pattern against a value, but I'm only interested in a true-false information on whether a value matches a pattern (e.g. a specific data type constructor). For instance:

data Color = 
    RGB Int Int Int 
  | Greyscale Int

toHex :: Color -> String
toHex color =
  if isGreyscale color then something
  else somethingElse

  where
  isGreyscale :: Color -> Bool
  isGreyscale (Greyscale _) = True
  isGreyscale _             = False

whereas I'd like to do do the pattern matching without creating an unnecessary auxillary function, something along the lines of:

toHex :: Color -> String
toHex color =
  if (color ~~ (Greyscale _)) then something
  else somethingElse

Is there a specific syntax allowing something similar to the example above? Or perhaps an idiom that would come in handy in such situations?

3
Unless we already have a boolean around, if is often pretty bad, since it forces us to take our rich data and strip everything away until we have a boolean. if condition is essentially a very limited case condition of True -> ... ; False -> ..., which unlike the general case never binds values to variables -- a key part of pattern matching. In your case, to get a boolean you are throwing away the value inside Grayscale value, when a case would keep it. Don't suffer from boolean blindness! - chi
May be interesting reading for you: A Crossroad at a Branch. - Daniel Wagner
An ugly but occasionally useful idiom: if null [ () | Greyscale _ <- color ] ... - luqui

3 Answers

4
votes

I don't believe there is (or can be) an infix operator, because a pattern isn't a value; it's syntax.

You are looking for a case expression

toHex :: Color -> String
toHex color = case color of
               Greyscale _ -> something
               otherwise -> somethingElse

though you would more commonly write this as

toHex :: Color -> String
toHex (Greyscale _) = something
toHex _ = somethingElse

which essentially desugars to the code above.

There is also the LambdaCase extension in GHC, which would allow you to write the following, eliminating the otherwise unnecessary variable color.

{-# LANGUAGE LambdaCase #-}


toHex :: Color -> String
toHex = \case 
          Greyscale _ -> something
          otherwise -> somethingElse
2
votes

You can use pattern matching in the function definition to determine the value:

toHex :: Color -> String
toHex (Greyscale _) = something
toHex _ = somethingElse

The first pattern matches a grey scale value regardless of the integer value, and the second clause matches everything else. If something and somethingElse are functions that require details from the parameter, you can capture those easily:

toHex :: Color -> String
toHex (Greyscale g) = something g
toHex (RGB r g b) = somethingElse r g b
1
votes

You can use multi-way if-expressions and pattern guards to get almost exactly the syntax you want:

{-# LANGUAGE MultiWayIf #-}

toHex :: Color -> String
toHex color =
  if | Greyscale _ <- color -> something
     | otherwise -> somethingElse