Source: Hutton, Graham. "Programming in Haskell" (p. 180)
- Using
getCh, define an actionreadLine :: IO Stringthat behaves in the same way as getLine, except that it also permits the delete key to be used to remove characters.
Hint: the delete character is
’\ DEL’, and the control character for moving the cursor back one space is’\b’.
I solved this exercise using one '\b' character, but found online that a solver used two. Why the solver of this problem uses "\b \b" instead of "\b" ? Seems like a typo but I am unsure. I have found it works with three '\b' characters.
How does this character work ?
import System.IO
getCh :: IO Char
getCh = do
hSetEcho stdin False
x <- getChar
hSetEcho stdin True
return x
readLine :: IO String
readLine = readLine' ""
readLine' :: String -> IO String
readLine' xs = do
x <- getCh
case x of
'\n' -> do
putChar '\n'
return xs
'\DEL' ->
if null xs
then readLine' ""
else do
putStr "\b \b"
readLine' (init xs)
_ -> do
putChar x
readLine' (xs ++ [x])
If you use just "\b", the cursor goes to the left but doesn't delete the character seen there until you overwrite it with a new key input. For example, FOO← will end you up with fo⁁o, but that's misleading: if you don't hit another letter key but immediately ↵, then it'll seem that the result is still foo, when actually it's only fo.
To avoid this, the solution "\b \b" moves to the left, overwrites the character with a space to visualise deleting it, and immediately moves to the left again. Together this has the same effect as moving left once and deleting the character there in-place.
\b moves the cursor one character back, but it doesn't erase it (at least, not on most terminals). For example, the string abcde\b will be displayed as abcde, and the string abcde\bf as abcdf. That's why the sequence \b \b explicitly overwrites the last character with a space, and then moves the cursor back again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With