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