Strings ([Char]) / words / lines
In Haskell, String is a type alias for [Char] (a list of Char). This means all list operation functions can be used directly on strings. Functions like words (split on whitespace), lines (split on newlines), and their inverses unwords and unlines allow concise text processing.
Syntax
-- ----------------------------------------------- -- String is a type alias for [Char] -- ----------------------------------------------- -- A string literal is equivalent to a list of Char "abc" == ['a', 'b', 'c'] -- True -- A single character is of type Char (single quotes) -- A string is a list of Char (double quotes) 'a' :: Char "abc" :: String -- which means [Char] -- ----------------------------------------------- -- Splitting and joining strings -- ----------------------------------------------- -- words — splits on whitespace (spaces, tabs, newlines) -- Result is [String] words "Yuji Itadori" -- ["Yuji", "Itadori"] -- lines — splits on '\n' lines "Yuji\nMegumi\nNobara" -- ["Yuji", "Megumi", "Nobara"] -- unwords — joins [String] with a single space unwords ["Yuji", "Itadori"] -- "Yuji Itadori" -- unlines — joins [String] appending '\n' to each element unlines ["Yuji", "Megumi"] -- "Yuji\nMegumi\n" -- ----------------------------------------------- -- length / reverse / take / drop -- ----------------------------------------------- -- String is a list, so general list functions apply length "Sukuna" -- 6 reverse "Sukuna" -- "anuKuS" take 5 "Ryomen Sukuna" -- "Ryome" drop 7 "Ryomen Sukuna" -- "Sukuna"
Function List
| Function / Operator | Type Signature | Description |
|---|---|---|
words | String -> [String] | Splits a string into a list of words on whitespace. |
lines | String -> [String] | Splits a string into a list of lines on '\n'. |
unwords | [String] -> String | Joins a list of strings with a single space. |
unlines | [String] -> String | Joins a list of strings, appending '\n' to each element. |
length | [a] -> Int | Returns the number of characters (Char count) in the string. |
reverse | [a] -> [a] | Returns a new string with the characters in reverse order. |
take n | Int -> [a] -> [a] | Takes the first n characters from the string. |
drop n | Int -> [a] -> [a] | Returns the string after dropping the first n characters. |
concat | [[a]] -> [a] | Concatenates a [String] with no separator. |
concatMap | (a -> [b]) -> [a] -> [b] | Applies a function to each element and concatenates the results. |
elem c | Char -> String -> Bool | Returns True if character c is found in the string. |
filter | (a -> Bool) -> [a] -> [a] | Returns a string keeping only the Char elements that satisfy the predicate. |
map | (a -> b) -> [a] -> [b] | Returns a new string by applying a function to each Char. |
null | [a] -> Bool | Returns True if the string is empty. |
Sample Code
jjk_string_basic.hs
-- jjk_string_basic.hs — Sample demonstrating basic String / Char operations
-- Uses Jujutsu Kaisen character names as sample data
-- to demonstrate words / lines / unwords / unlines and more
--
-- Compile and run:
-- ghc jjk_string_basic.hs -o jjk_string_basic && ./jjk_string_basic
module Main where
-- -----------------------------------------------
-- Constant definitions
-- -----------------------------------------------
-- Full name of a sorcerer (space-separated)
itadoriFullName :: String
itadoriFullName = "Yuji Itadori"
sukunaFullName :: String
sukunaFullName = "Ryomen Sukuna"
-- Member list joined by newlines
teamRaw :: String
teamRaw = "Yuji Itadori\nMegumi Fushiguro\nNobara Kugisaki"
-- Technique name list separated by spaces
techniqueRaw :: String
techniqueRaw = "Divergent Fist Black Flash Mahoraga"
-- -----------------------------------------------
-- Verifying that String is [Char]
-- -----------------------------------------------
-- Extract the first character of a string (head works on [Char])
firstChar :: String -> Char
firstChar s = head s
-- Returns the character count of a string
charCount :: String -> Int
charCount = length
-- -----------------------------------------------
-- Entry point
-- -----------------------------------------------
main :: IO ()
main = do
-- ----- Verify that String is [Char] -----
putStrLn "===== String is [Char] ====="
putStrLn $ "itadoriFullName : " ++ itadoriFullName
putStrLn $ "head itadoriFullName : " ++ [firstChar itadoriFullName]
putStrLn $ "length itadoriFullName : " ++ show (charCount itadoriFullName)
putStrLn $ "reverse sukunaFullName : " ++ reverse sukunaFullName
putStrLn $ "take 6 sukunaFullName : " ++ take 6 sukunaFullName
putStrLn $ "drop 7 sukunaFullName : " ++ drop 7 sukunaFullName
-- ----- words / unwords -----
putStrLn "\n===== words / unwords ====="
let techWords = words techniqueRaw
putStrLn $ "techniqueRaw : " ++ techniqueRaw
putStrLn $ "words techniqueRaw : " ++ show techWords
putStrLn $ "unwords techWords : " ++ unwords techWords
-- ----- lines / unlines -----
putStrLn "\n===== lines / unlines ====="
let members = lines teamRaw
putStrLn $ "teamRaw (with newlines) :"
putStr teamRaw
putStrLn "\n"
putStrLn $ "lines teamRaw : " ++ show members
putStr $ "unlines members :\n" ++ unlines members
-- ----- filter / map -----
putStrLn "===== filter / map ====="
-- Remove all space characters
let noSpace = filter (/= ' ') itadoriFullName
putStrLn $ "filter (/= ' ') ... : " ++ noSpace
-- Convert all characters to lowercase (simple ASCII conversion)
-- Uses ord / chr for a basic conversion
let toLowerAscii c
| c >= 'A' && c <= 'Z' = toEnum (fromEnum c + 32)
| otherwise = c
let lowered = map toLowerAscii itadoriFullName
putStrLn $ "map toLower ... : " ++ lowered
-- ----- Search for a character with elem -----
putStrLn "\n===== elem ====="
putStrLn $ "'Y' `elem` itadori : " ++ show ('Y' `elem` itadoriFullName)
putStrLn $ "'z' `elem` itadori : " ++ show ('z' `elem` itadoriFullName)
-- ----- concat / concatMap -----
putStrLn "\n===== concat / concatMap ====="
let surnames = ["Itadori", "Fushiguro", "Kugisaki"]
putStrLn $ "concat surnames : " ++ concat surnames
-- Extract the initial of each name to build an initials string
let initials = concatMap (\name -> [head name, '.', ' ']) surnames
putStrLn $ "initials : " ++ initials
ghc jjk_string_basic.hs -o jjk_string_basic && ./jjk_string_basic [1 of 1] Compiling Main ( jjk_string_basic.hs, jjk_string_basic.o ) Linking jjk_string_basic ... ===== String is [Char] ===== itadoriFullName : Yuji Itadori head itadoriFullName : Y length itadoriFullName : 13 reverse sukunaFullName : anuKuS nemouyR take 6 sukunaFullName : Ryomen drop 7 sukunaFullName : Sukuna ===== words / unwords ===== techniqueRaw : Divergent Fist Black Flash Mahoraga words techniqueRaw : ["Divergent","Fist","Black","Flash","Mahoraga"] unwords techWords : Divergent Fist Black Flash Mahoraga ===== lines / unlines ===== teamRaw (with newlines) : Yuji Itadori Megumi Fushiguro Nobara Kugisaki lines teamRaw : ["Yuji Itadori","Megumi Fushiguro","Nobara Kugisaki"] unlines members : Yuji Itadori Megumi Fushiguro Nobara Kugisaki ===== filter / map ===== filter (/= ' ') ... : YujiItadori map toLower ... : yuji itadori ===== elem ===== 'Y' `elem` itadori : True 'z' `elem` itadori : False ===== concat / concatMap ===== concat surnames : ItadoriFushiguroKugisaki initials : I. F. K.
jjk_string_split.hs
-- jjk_string_split.hs — Implements a function to split a string on an arbitrary delimiter
-- Because words / lines only handle fixed delimiters,
-- this implements a general-purpose version using recursion to understand how words works
--
-- Compile and run:
-- ghc jjk_string_split.hs -o jjk_string_split && ./jjk_string_split
module Main where
-- -----------------------------------------------
-- Split function for an arbitrary delimiter
-- -----------------------------------------------
-- splitOn sep str — splits str into [String] using sep as delimiter
-- Consecutive separators produce empty string tokens
splitOn :: Char -> String -> [String]
splitOn _ "" = [""]
splitOn sep str =
let (token, rest) = break (== sep) str
in token : case rest of
[] -> []
(_:xs) -> splitOn sep xs
-- joinWith sep strs — joins [String] using sep as delimiter
joinWith :: Char -> [String] -> String
joinWith _ [] = ""
joinWith _ [x] = x
joinWith sep (x:xs) = x ++ [sep] ++ joinWith sep xs
-- -----------------------------------------------
-- Entry point
-- -----------------------------------------------
main :: IO ()
main = do
-- ----- Split a comma-delimited technique name list -----
let csvRow = "Divergent Fist,Black Flash,Mahoraga,Hollow Purple"
let techniques = splitOn ',' csvRow
putStrLn "===== splitOn ',' ====="
putStrLn $ "Input : " ++ csvRow
putStrLn $ "Split : " ++ show techniques
-- ----- Rejoin to recover the original string -----
let rejoined = joinWith ',' techniques
putStrLn $ "Rejoined : " ++ rejoined
putStrLn $ "Matches original: " ++ show (csvRow == rejoined)
-- ----- Split a slash-delimited path-style string -----
let path = "jujutsu/tokyo/itadori"
let segments = splitOn '/' path
putStrLn "\n===== splitOn '/' ====="
putStrLn $ "Input : " ++ path
putStrLn $ "Split : " ++ show segments
-- ----- Comparison with words (differs in handling consecutive spaces) -----
let spaced = "Yuji Itadori" -- two spaces
putStrLn "\n===== words vs splitOn ' ' ====="
putStrLn $ "Input : \"" ++ spaced ++ "\""
putStrLn $ "words spaced : " ++ show (words spaced)
putStrLn $ "splitOn ' ' spaced : " ++ show (splitOn ' ' spaced)
-- words treats consecutive spaces as a single separator
-- splitOn produces an empty string token between consecutive separators
ghc jjk_string_split.hs -o jjk_string_split && ./jjk_string_split [1 of 1] Compiling Main ( jjk_string_split.hs, jjk_string_split.o ) Linking jjk_string_split ... ===== splitOn ',' ===== Input : Divergent Fist,Black Flash,Mahoraga,Hollow Purple Split : ["Divergent Fist","Black Flash","Mahoraga","Hollow Purple"] Rejoined : Divergent Fist,Black Flash,Mahoraga,Hollow Purple Matches original: True ===== splitOn '/' ===== Input : jujutsu/tokyo/itadori Split : ["jujutsu","tokyo","itadori"] ===== words vs splitOn ' ' ===== Input : "Yuji Itadori" words spaced : ["Yuji","Itadori"] splitOn ' ' spaced : ["Yuji","","Itadori"]
Overview
In Haskell, String is a type alias for [Char], so all list operation functions apply to strings directly. words treats consecutive whitespace as a single separator, while lines splits on '\n' boundaries. The inverse unwords joins with a single space, and unlines appends '\n' to each element before joining. General-purpose list functions such as filter, map, and concatMap can be applied directly to String, enabling a wide range of text processing without external libraries. For case conversion, use toUpper and toLower from Data.Char. For regex-based search and replacement, use packages in the Text.Regex family. For performance-critical workloads, Data.Text (from the text package) is worth considering, as it uses a different internal representation with better memory efficiency and throughput. For the basics of lists, see List Basics ([] and :). For string conversion with show and read, see show and read.
Common Mistake 1: Applying head to an empty string causes an exception
Because String is a [Char] list, calling head on an empty string "" throws a runtime exception (Prelude.head: empty list). Check for emptiness with null before using head, or handle it safely with pattern matching.
headEmpty.hs
module Main where getInitial :: String -> Char getInitial s = head s -- Passing an empty string causes an exception main :: IO () main = do print (getInitial "")
ghc headEmpty.hs -o headEmpty && ./headEmpty headEmpty: Prelude.head: empty list
When the string may be empty, use pattern matching or a null check to handle it safely.
headEmptyOk.hs
module Main where -- Handle an empty string safely with pattern matching getInitial :: String -> Maybe Char getInitial [] = Nothing getInitial (c:_) = Just c main :: IO () main = do print (getInitial "") -- Nothing print (getInitial "Itadori") -- Just 'I'
ghc headEmptyOk.hs -o headEmptyOk && ./headEmptyOk Nothing Just 'I'
Common Mistake 2: Confusing Char and String quotes
In Haskell, 'a' (single quotes) is a Char, while "a" (double quotes) is a String (= [Char]). Although they look similar, they are different types, and mixing them causes a compile error.
charStringMix.hs
module Main where
main :: IO ()
main = do
-- 'Y' is Char, "uji" is String
-- Trying to concatenate Char and String directly with ++ causes an error
putStrLn ('Y' ++ "uji")
ghc charStringMix.hs -o charStringMix Couldn't match type 'Char' with '[Char]'
Use the : operator to prepend a Char to a String, or wrap it in a list with [c].
charStringMixOk.hs
module Main where
main :: IO ()
main = do
-- Use : to prepend a Char to the front of a String
putStrLn ('Y' : "uji") -- "Yuji"
-- You can also wrap in a list and use ++
putStrLn (['Y'] ++ "uji") -- "Yuji"
ghc charStringMixOk.hs -o charStringMixOk && ./charStringMixOk Yuji Yuji
Common Mistake 3: Index access via !! is O(n)
Because String is a linked list, str !! n (accessing the nth character) is an O(n) operation that traverses from the head. Heavy use of index access on large strings can affect performance. When frequent random access is needed, Data.Text is worth considering.
indexAccess.hs
module Main where main :: IO () main = do let name = "Fushiguro Megumi" -- !! is O(n) — traverses n elements from the head print (name !! 0) -- 'F' print (name !! 10) -- 'M' (traverses 10 elements from the head) -- Indexes near the end are especially costly print (name !! (length name - 1)) -- 'i'
ghc indexAccess.hs -o indexAccess && ./indexAccess 'F' 'M' 'i'
When processing elements sequentially, use map or zipWith. To extract a specific element, combine take and drop to avoid !!.
Overview
If you find any errors or copyright issues, please contact us.