It is common to shorten city names when writing, or at least in Sweden.

  • "Göteborg" (Gothenburg) is often referred to as "Gbg"
  • "Uddevalla" is commonly written as "Ua"
  • "Trollhättan" is often written as "Thn"
  • ...and a whole lot of other city names are often shortened

"Uddevalla" could also be shortened as "Uva", "Uvalla", or even "Udl" (although I've never seen anyone actually type that, according to my rules here it would be allowed). "vd" however would not be allowed as the letters must appear in order, "dv" would be allowed as an abbreviation for "Uddevalla" as "v" comes after "d".

Given a list of cities (or more generally: Strings) and a search string, print the city names that can be constructed by using these abbreviation rules.

Example cities (my townnames.txt file):

Stora Höga

These are cities in Sweden, mostly in around my area. With the above list and the search string "Sm" the following cities are printed:

["Strömstad", "Stockholm", "Simrishamn"]

Instead given the string "Vbg" the results are:

["Vänersborg", "Varberg"]

After looking at the review I got from my previous Haskell question I wrote the following code to accomplish this:

module Main

nameMatch :: [Char] -> [Char] -> Bool
nameMatch [] [] = True
nameMatch s [] = False
nameMatch [] s = True
nameMatch search@(s:restSearch) (c:city)
   | c == s    = nameMatch restSearch city
   | otherwise = nameMatch search city

main = do
  putStrLn "Loading list of towns..."
  text <- readFile "townnames.txt"
  putStrLn "Enter search phrase:"
  search <- getLine
  let cities = lines text
  putStrLn $ show $ filter (nameMatch search) cities

Any possible improvements or suggestions welcome.


2 Answers 2


[Char] is usually written as String.

You have a redundant base case: nameMatch [] [].

The parameter names are a bit off. A common convention for naming the head and tail is x:xs.

nameMatch :: String -> String -> Bool
nameMatch [] _      = True
nameMatch abbrev [] = False
nameMatch abbrev@(a:as) word@(w:ws)
   | a == w    = nameMatch as ws
   | otherwise = nameMatch abbrev ws

For printing the output, if you want each result to be on a separate line rather than as a list (which would produce output like ["Trollh\228ttan"]), then write

mapM putStrLn $ filter (nameMatch search) cities
  • 1
    \$\begingroup\$ If I would remove the redundant base case, "Stockholm" would not match with "Sm". I am not quite sure why exactly, but I had to insert that to fix that bug. \$\endgroup\$ Commented Nov 26, 2015 at 22:11
  • \$\begingroup\$ So I should use String? Considering an answer to my previous Haskell question, I thought it was better to handle that as [Char] \$\endgroup\$ Commented Nov 26, 2015 at 22:12
  • \$\begingroup\$ Oops, the order of the base cases matters. Fixed the "Stockholm" bug in Rev 2. \$\endgroup\$ Commented Nov 26, 2015 at 22:15
  • 1
    \$\begingroup\$ bisserlis seemed to waffle between [Char] and String. I don't know why. Probably because one of the functions in that question was called sumOfChars. In nameMatch, it's probably more useful to think of them as strings. The difference is purely stylistic, though, as type String = [Char] is a "typedef". \$\endgroup\$ Commented Nov 26, 2015 at 22:19
  • \$\begingroup\$ A common naming convention it might be, but how descriptive is x:xs really? \$\endgroup\$ Commented Nov 27, 2015 at 7:18
putStrLn "Loading list of towns..."
text <- readFile "townnames.txt"

Because Haskell is lazy, the file won't actually be read until it's needed in nameMatch. Also, the file will not be loaded in its entirety into memory (assuming it's big enough), it's read chunk by chunk (which means the program uses the same amount of memory, regardless of the file's size!).

text <- readFile "townnames.txt"
let cities = lines text

You can avoid the intermediate text by using fmap:

cities <- lines <$> readFile "townnames.txt"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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