Find labels in string in Haskell I wrote code that parses a table header string in order to obtain two string tags, one for variable name (like GDP) and another for unit of measurement (like bln_rub). The tags collectively make a unique identifier for the table data.

Perhaps something can be done better/shorter/more easily readable?

import Data.List (isInfixOf)

type Label = Maybe String

data Variable = Variable
    { name :: Label,
      unit :: Label
    } deriving (Show, Eq)

makeVariable :: String -&gt; String -&gt; Variable 
makeVariable name unit = Variable (Just name) (Just unit) 

isDefined:: Variable -&gt; Bool
isDefined var = (name var /= Nothing) &amp;&amp; (unit var /= Nothing) 

isIdentical:: Variable -&gt; String -&gt; String -&gt; Bool
isIdentical var name unit = (makeVariable name unit) == var  

-- Map allows a readable view of tuple-like 
-- associative structure.     
data Map = Map 
    { label :: String,
      texts :: [String] -- note: can use non-empty List 
    } deriving (Show)

nameMaps = [
      Map "GDP" ["Gross domestic product"]
    , Map "INDPRO" ["Industrial production"]
    ]

unitMaps = [
      Map "bln_rub" ["bln rub", "billion ruble"]
    , Map "rog" ["% change to previous period"] -- rate of growth
    ] 

-- COMMENT: code below converts nameMaps and unitMaps
--          to list of tuples which are used for searching a header
asTuples :: Map -&gt; [(String, String)]   
asTuples (Map label texts) = [(text, label) | text &lt;- texts]  

findAllKeys :: [(String, String)] -&gt; String -&gt; [String]
findAllKeys mapper header = [key | tup@(text, key) &lt;- mapper,  
                                text `isInfixOf` header]

getLabel :: [Map] -&gt; String -&gt; Label
getLabel maps' header = case findAllKeys (flatten' maps') header of 
        [] -&gt; Nothing
        (x:_) -&gt; Just x
    where flatten' = concatMap asTuples

getName = getLabel nameMaps
getUnit = getLabel unitMaps
parseHeader text = Variable (getName text) (getUnit text)

x = parseHeader "Gross domestic product, bln rub"
flag = (isDefined x) &amp;&amp; (isIdentical x "GDP" "bln_rub")
raise x = error ("Something wrong with: " ++ show x) 

main = do
    if flag then putStrLn $ show x else
      raise x   
    return ()
Perhaps something can be done shorter


A trick that I often use to avoid creating "filled" objects is simply matching on Just:

isIdentical:: Variable -&gt; String -&gt; String -&gt; Bool
isIdentical (Variable (Just n) (Just u)) n' u' = n == n' &amp;&amp; u == u'
isIdentical _ _ _ = False


You can even do the actual check inside the pattern guard:

(Variable (Just n) (Just u)) n' u' | n == n' &amp;&amp; u == u' = True


But that seems like an overkill.

Not saying that this change is particularly warranted in this specific case, but it's a nice tool to have, I think.