Skip to content
AgileLogics
5 min read

Strings and Characters

Prerequisites: List functions

Since String is [Char], strings use all the list functions. A few extra functions are specific to text.

Words and lines

main :: IO ()
main = do
  let sentence = "  the quick   brown fox  "
  print (words sentence)
  putStrLn (unwords (words sentence))
  print (lines "first\nsecond\nthird")
  putStr (unlines ["one", "two"])
["the","quick","brown","fox"]
the quick brown fox
["first","second","third"]
one
two

words splits on any whitespace and drops the empty pieces; unwords joins with single spaces. lines/unlines do the same with newlines.

Data.Char

import Data.Char (toUpper, toLower, isDigit, isAlpha, isSpace, ord, chr, digitToInt)

capitalize :: String -> String
capitalize "" = ""
capitalize (c : cs) = toUpper c : map toLower cs

main :: IO ()
main = do
  putStrLn (capitalize "hASKELL")
  print (filter isDigit "Call 555-0123 now")
  print (all isAlpha "Haskell", any isSpace "no spaces")
  print (ord 'A', chr 97)
  print (sum (map digitToInt "2024"))
Haskell
"5550123"
(True,True)
(65,'a')
8

show and read

show turns a value into a string; read parses one — you must say which type you want:

main :: IO ()
main = do
  putStrLn (show 3.5 ++ " and " ++ show True)
  print (read "42" + 1 :: Int)
  print (read "[1,2,3]" :: [Int])
  print (read "(1,\"one\")" :: (Int, String))
3.5 and True
43
[1,2,3]
(1,"one")

read crashes on bad input (read "abc" :: Int). For user input, use readMaybe from Text.Read, which returns Nothing instead:

import Text.Read (readMaybe)

main :: IO ()
main = do
  print (readMaybe "42" :: Maybe Int)
  print (readMaybe "4x2" :: Maybe Int)
  print (readMaybe "3.5" :: Maybe Double)
Just 42
Nothing
Just 3.5

Escapes and multi-line strings

"\n" (newline), "\t" (tab), "\"" (quote), and "\\" (backslash) work as in most languages. Since GHC 9.12, the MultilineStrings extension adds triple-quoted """ strings; traditionally, long strings are built with unlines or ++.

String versus Text

String is simple but slow and memory-hungry for large amounts of text, because each character is a list cell. Real applications use Data.Text from the text package, covered in Level 2.

Practice

Write isPalindrome :: String -> Bool that ignores case, spaces, and punctuation, and test it on "A man, a plan, a canal: Panama".

Show solution
import Data.Char (isAlpha, toLower)

isPalindrome :: String -> Bool
isPalindrome s = cleaned == reverse cleaned
  where
    cleaned = map toLower (filter isAlpha s)

main :: IO ()
main = do
  print (isPalindrome "A man, a plan, a canal: Panama")
  print (isPalindrome "Haskell")
True
False