Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 511
orome Avatar asked Aug 22 '15 16:08

orome


People also ask

Does Haskell have pattern matching?

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.

Is maybe a data constructor in Haskell?

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 .

How does pattern matching work in Haskell?

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.

Is Io a data constructor in Haskell?

IO is a type constructor, not a value constructor.


2 Answers

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
like image 114
Daniel Wagner Avatar answered Oct 19 '22 10:10

Daniel Wagner


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

like image 43
Petr Avatar answered Oct 19 '22 08:10

Petr