views:

133

answers:

3

I'm writing a parsec parser which reads in strings and converts escaped characters, as part of exercise 3 here.

For that exercise I am using this function:

escapedCharFromChar :: Char -> Char
escapedCharFromChar c = read $ concat ["'\\",[c],"'"]

I am not to impressed with the use of read to convert the character x into the escape character with the name x. Can anyone suggest a more elegant function of type Char -> Char to do this?

+1  A: 

I just used pattern matching for the few escapes I cared about - i.e. 't' -> '\t'etc. The solution other readers suggested were similar. Not very generic, but very straight-forward.

delnan
+3  A: 

One way is to lay out the cases exhaustively:

charFromEscape :: Char -> Char
charFromEscape 'n' = '\n'
charFromEscape 't' = '\t'
--- ... --- Help!

You could also use lookup:

-- this import goes at the top of your source file
import Data.Maybe (fromJust)

charFromEscape :: Char -> Char
charFromEscape c = fromJust $ lookup c escapes
  where escapes = [('n', '\n'), ('t', '\t')] -- and so on

The fromJust bit may look strange. The type of lookup is

lookup :: (Eq a) => a -> [(a, b)] -> Maybe b

which means for a value of some type over which equality is defined and a lookup table, it wants to give you the corresponding value from the lookup table—but your key isn't guaranteed to be present in the table! That's the purpose of Maybe, whose definition is

data Maybe a = Just a | Nothing

With fromJust, it assumes you got Just something (i.e., c has an entry in escapes), but this will fall apart when that assumption is invalid:

ghci> charFromEscape 'r'
*** Exception: Maybe.fromJust: Nothing

These examples will move you along in the exercise, but it's clear that you'd like better error handling. Also, if you expect the lookup table to be large, you may want to look at Data.Map.

Greg Bacon
+3  A: 

read (or rather, Text.Read.Lex.lexCharE) is how you get at GHC's internal table, which is defined as:

 lexEscChar =
   do c <- get
      case c of
        'a'  -> return '\a'
        'b'  -> return '\b'
        'f'  -> return '\f'
        'n'  -> return '\n'
        'r'  -> return '\r'
        't'  -> return '\t'
        'v'  -> return '\v'
        '\\' -> return '\\'
        '\"' -> return '\"'
        '\'' -> return '\''
        _    -> pfail

Eventually, you have to define the semantics somewhere. You can do it in your program, or you can reuse GHC's.

jrockway
It's a shame `lexEscChar` isn't exported otherwise this would have been the perfect answer. Thanks for the heads up though.
Dave Tapley