Chapter 8 - Exercise with Exceptions

7 views
Skip to first unread message

TomAmundsen

unread,
Mar 20, 2009, 2:37:25 PM3/20/09
to Real World Haskell Book Club
Has anyone written a solution to #1 in the third group of exercises?

The one that asks you to re-write `globToRegex' so that it has the
following type signature:
globToRegex :: String -> Either GlobError String

I am quite stumped on this one.

In case you don't want to look it up in the book, here is the source
you're supposed to modify it from:

[code]
module GlobRegex
(
globToRegex
, matchesGlob
) where

import Text.Regex.Posix ((=~))

globToRegex :: String -> String

globToRegex cs = '^' : globToRegex' cs ++ "$"

globToRegex' :: String -> String
globToRegex' "" = ""

globToRegex' ('*':cs) = ".*" ++ globToRegex' cs

globToRegex' ('?':cs) = '.' : globToRegex' cs

globToRegex' ('[':'!':c:cs) = "[^" ++ c : charClass cs
globToRegex' ('[':c:cs) = '[' : c : charClass cs
globToRegex' ('[':_) = error "unterminated character class"

globToRegex' (c:cs) = escape c ++ globToRegex' cs

escape :: Char -> String
escape c | c `elem` regexChars = '\\' : [c]
| otherwise = [c]
where regexChars = "\\+()^$.{}]|"

charClass :: String -> String
charClass (']':cs) = ']' : globToRegex' cs
charClass (c:cs) = c : charClass cs
charClass [] = error "unterminated character class"

matchesGlob :: FilePath -> String -> Bool
name `matchesGlob` pat = name =~ globToRegex pat
[/code]

Tim Scheffler

unread,
Mar 20, 2009, 3:35:29 PM3/20/09
to real-world-has...@googlegroups.com
I think it's been asked to "enhance" all the function with the Either
data type.
I have not done this exercise (shame on me ;-), but I would go along
these lines:

globToRegex :: String -> Either GlobError String

globToRegex cs =
case globToRegex' cs of
Left err -> Left err
Right inner -> Right $ '^' : inner ++ "$"

Now you have to wrap the globToRegex' also

globToRegex' :: String -> Either GlobError String

so taking two examples:

globToRegex' "" = Right ""
globToRegex' ('*':cs) = case globToRegex' cs of
Left err -> Left err
Right inner -> Right $ ".*" ++ inner

and wrapping an "error" case:

globToRegex' ('[':_) = Left "unterminated character class"


This unwrapping/wrapping has to be done for every function in the
original code, which is kind of tedious as said in the book.

But all this is untested and I might be completely wrong ;-)

I hope problems like this will become much easier once monads will be
ready for use.

Tim

TomAmundsen

unread,
Mar 23, 2009, 2:02:18 PM3/23/09
to Real World Haskell Book Club
Yea. I had this sort of solution in mind. I just didn't think it was
the best way to do it, since it involves so much redundant code.

Anyhow, I suppose it is the best solution I can think of at the
moment. Here is the code:

type GlobError = String

globToRegex :: String -> Either GlobError String

globToRegex cs = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ '^' : result ++ "$"

globToRegex' :: String -> Either GlobError String
globToRegex' "" = Right ""

globToRegex' ('*':cs) = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ ".*" ++ result

globToRegex' ('?':cs) = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ '.' : result

globToRegex' ('[':'!':c:cs) = case charClass cs of
Left err -> Left err
Right result -> Right $ "[^" ++ c :
result

globToRegex' ('[':c:cs) = case charClass cs of
Left err -> Left err
Right result -> Right $ '[' : c :
result

globToRegex' ('[':_) = Left "unterminated character class"

globToRegex' (c:cs) = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ escape c ++ result

escape :: Char -> String
escape c | c `elem` regexChars = '\\' : [c]
| otherwise = [c]
where regexChars = "\\+()^$.{}]|"

charClass :: String -> Either GlobError String
charClass (']':cs) = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ ']' : result

charClass (c:cs) = case charClass cs of
Left err -> Left err
Right result -> Right $ c : result
charClass [] = Left "unterminated character class"

John Goerzen

unread,
Mar 23, 2009, 2:12:16 PM3/23/09
to real-world-has...@googlegroups.com
TomAmundsen wrote:
> Yea. I had this sort of solution in mind. I just didn't think it was
> the best way to do it, since it involves so much redundant code.

You might be interested to know that you can make a monad out of an Either.

One piece of your code might then read:

globToRegex' ('*':cs) =

do r <- globToRegex' cs
return $ ".*" ++ result

note that return produces the same effect as "Right" here.

And of course, remember that do is syntactic sugar for a more complex
representation.

See page 453 for details.

-- John

Reply all
Reply to author
Forward
0 new messages