orome
orome

Reputation: 48456

Can I match a data constructor wildcard in Haskell?

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?

Upvotes: 15

Views: 1834

Answers (2)

Daniel Wagner
Daniel Wagner

Reputation: 152707

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

Upvotes: 21

Petr
Petr

Reputation: 63359

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 Strings 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 Strings 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).

Upvotes: 5

Related Questions