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 -> String -> Variable 
makeVariable name unit = Variable (Just name) (Just unit) 

isDefined:: Variable -> Bool
isDefined var = (name var /= Nothing) && (unit var /= Nothing) 

isIdentical:: Variable -> String -> String -> 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 -> [(String, String)]   
asTuples (Map label texts) = [(text, label) | text <- texts]  

findAllKeys :: [(String, String)] -> String -> [String]
findAllKeys mapper header = [key | tup@(text, key) <- mapper,  
                                text `isInfixOf` header]

getLabel :: [Map] -> String -> Label
getLabel maps' header = case findAllKeys (flatten' maps') header of 
        [] -> Nothing
        (x:_) -> 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) && (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 ()

1 Answer 1


Perhaps something can be done shorter

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

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

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

(Variable (Just n) (Just u)) n' u' | n == n' && 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.

  • \$\begingroup\$ I think it is good reminder of pattern matching and avoiding duplicate work that is happening after one creates an object and then equates it to something. \$\endgroup\$
    – Evgeny
    Oct 3, 2018 at 20:53
  • \$\begingroup\$ A guard also seems a bit more readable. Thank you for the suggestions! \$\endgroup\$
    – Evgeny
    Oct 4, 2018 at 5:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.