I have
data Foo = X (String, Int) | A String | B String | C String | D String -- ...
and have defined
f (X (s, _)) =s
f (A s) = s
f (B s) = s
f (C s) = s
f (D s) = s
-- ...
but would prefer to be able to write something like
f (X (s, _)) =s
f (_ s) = s
But there seems to be no way to do this (I get a "parse error" associated with the _
).
Is there a way to match a data constructor wildcard in Haskell?
Overview. We use pattern matching in Haskell to simplify our codes by identifying specific types of expression. We can also use if-else as an alternative to pattern matching. Pattern matching can also be seen as a kind of dynamic polymorphism where, based on the parameter list, different methods can be executed.
Maybe is a type constructor, and its two possible data constructors are Nothing and Just . So you have to say Just 5 instead of Maybe 5 .
Pattern matching consists of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns. When defining functions, you can define separate function bodies for different patterns.
IO is a type constructor, not a value constructor.
Nope. But you can write this:
data Foo
= X { name :: String, age :: Int }
| A { name :: String }
| B { name :: String }
| C { name :: String }
| D { name :: String }
and then have name :: Foo -> String
. You could also consider this:
data Tag = A | B | C | D | X Int
data Foo = Tagged Tag String
f (Tagged _ s) = s
In addition to @DanielWagner's answer, an alternative is to use Scrap your boilerplate (AKA "SYB"). It allows you to find the first subterm of a given type. So you could define
{-# LANGUAGE DeriveDataTypeable #-}
import Control.Monad
import Data.Data
import Data.Generics.Schemes
import Data.Typeable
data Foo = X (String, Int) | A String | B String | C String | D String
deriving (Show, Eq, Ord, Data, Typeable)
fooString :: Foo -> Maybe String
fooString = msum . gmapQ cast
and fooString
will return the first String
argument of your constructors. Function cast
filters out String
s and gmapQ
gets the filtered values for all immediate subterms.
However, this won't return the String
from X
, because X
has no immediate String
subterm, it has only one subterm of type (String, Int)
. In order to get the first String
anywhere in the term hierarchy, you could use everywhere
:
fooString' :: Foo -> Maybe String
fooString' = everything mplus cast
Note that this approach is slightly fragile: It includes simply all String
s it finds, which might not be always what you want, in particular, if you later extend your data type (or some data type that it references).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With