
One of the exercises in Real World Haskell is to write a glob matcher
without translating it to regular expressions. This turns out to be a
lot more subtle than it looks (character classes like [!]-}] tend to
break things).
In doing this, I wrote a matchCharClass function for handling
character classes. The initial version had type:
matchCharClass :: Pattern -> Char -> (Bool, Pattern)
I.e. - it returned rest of pattern after the char class as well as a
match succeed/fail indicator. Upon reflection, I realized that only
one of the two was ever used, so rewrote it to be:
matchCharClass :: Pattern -> Char -> Either Bool Pattern
This made the calling code a little larger - taking a apart tuples is
a bit easier than taking apart Either's - but simplified the values
being generated. I then realized that the code only used one of the
Bool values: if it was True, then Pattern got used instead. So I
rewrote it a third time, giving:
data CharClass = Fail | Pattern String
matchCharClass :: Pattern -> Char -> CharClass
This only required minor changes to the code, but made it easy to add
"Error String" to the CharClass datatype later. That version can be
seen at http://pastebin.com/eyre8795 (as always, critiques welcome).
I'd like to hear what more experienced haskell programmers have to say
about those three ways of returning multiple values.
Thanks,

Sounds like a Maybe to me...
On Tue, Apr 5, 2011 at 1:41 PM, Mike Meyer
One of the exercises in Real World Haskell is to write a glob matcher without translating it to regular expressions. This turns out to be a lot more subtle than it looks (character classes like [!]-}] tend to break things).
In doing this, I wrote a matchCharClass function for handling character classes. The initial version had type:
matchCharClass :: Pattern -> Char -> (Bool, Pattern)
I.e. - it returned rest of pattern after the char class as well as a match succeed/fail indicator. Upon reflection, I realized that only one of the two was ever used, so rewrote it to be:
matchCharClass :: Pattern -> Char -> Either Bool Pattern
This made the calling code a little larger - taking a apart tuples is a bit easier than taking apart Either's - but simplified the values being generated. I then realized that the code only used one of the Bool values: if it was True, then Pattern got used instead. So I rewrote it a third time, giving:
data CharClass = Fail | Pattern String matchCharClass :: Pattern -> Char -> CharClass
This only required minor changes to the code, but made it easy to add "Error String" to the CharClass datatype later. That version can be seen at http://pastebin.com/eyre8795 (as always, critiques welcome).
I'd like to hear what more experienced haskell programmers have to say about those three ways of returning multiple values.
Thanks,
http://www.mired.org/consulting.html Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- Alex R

I, for one, have a simplified view of the three that often proves helpful for fast decisions: - tuples are a sort of quick-and-dirty tool that gets the job done (simple to code, accessors and constructor already there). They don't stand well for refactoring and maintenance. - A simple, custom datatype like yours is more explicit, easier to read (later, after you wrote the program), and flexible. It requires bit more code to write, and names (good names, please!) thought out. - Apart from being a good container, Either has an Monad instance written for you. It allows data-on-the-side :). This can prove incredibly valuable in *some* situations. Think through if it's worth it (it can cripple your code if your not sure about the "shape" data should take). I hope I'm expressing clearly enough (this subject borders subjectivity). El mar, 05-04-2011 a las 13:41 -0400, Mike Meyer escribió:
One of the exercises in Real World Haskell is to write a glob matcher without translating it to regular expressions. This turns out to be a lot more subtle than it looks (character classes like [!]-}] tend to break things).
In doing this, I wrote a matchCharClass function for handling character classes. The initial version had type:
matchCharClass :: Pattern -> Char -> (Bool, Pattern)
I.e. - it returned rest of pattern after the char class as well as a match succeed/fail indicator. Upon reflection, I realized that only one of the two was ever used, so rewrote it to be:
matchCharClass :: Pattern -> Char -> Either Bool Pattern
This made the calling code a little larger - taking a apart tuples is a bit easier than taking apart Either's - but simplified the values being generated. I then realized that the code only used one of the Bool values: if it was True, then Pattern got used instead. So I rewrote it a third time, giving:
data CharClass = Fail | Pattern String matchCharClass :: Pattern -> Char -> CharClass
This only required minor changes to the code, but made it easy to add "Error String" to the CharClass datatype later. That version can be seen at http://pastebin.com/eyre8795 (as always, critiques welcome).
I'd like to hear what more experienced haskell programmers have to say about those three ways of returning multiple values.
Thanks,

Am 05.04.2011 19:41, schrieb Mike Meyer: [...]
data CharClass = Fail | Pattern String matchCharClass :: Pattern -> Char -> CharClass
As mentioned by Alex Rozenshteyn this CharClass is isomorphic to "Maybe String".
This only required minor changes to the code, but made it easy to add "Error String" to the CharClass datatype later. That version can be
Replacing Fail (or Nothing) by "Error String" is like going to "Either String String". Yet, user-defined data types (no type synonyms!) may increase readability (and type safety). However, one disadvantage is that some type class instances have to be redefined (or derived) if needed. "Maybe" and "Either String" are fairly standard (and have Monad and what not instances), still your data type CharClass is perfect (if it serves the purpose). HTH Christian
seen at http://pastebin.com/eyre8795 (as always, critiques welcome).
I'd like to hear what more experienced haskell programmers have to say about those three ways of returning multiple values.
Thanks,

On Wed, 06 Apr 2011 11:24:45 +0200
Christian Maeder
Am 05.04.2011 19:41, schrieb Mike Meyer: [...]
data CharClass = Fail | Pattern String matchCharClass :: Pattern -> Char -> CharClass
As mentioned by Alex Rozenshteyn this CharClass is isomorphic to "Maybe String".
I think I missed the mail from Alex. But yes, that makes sense. And learning how to use the built-in types is part of learning the language.
This only required minor changes to the code, but made it easy to add "Error String" to the CharClass datatype later. That version can be
Replacing Fail (or Nothing) by "Error String" is like going to "Either String String".
Ok this confuses me. What does "or Nothing" by "Error String" mean? data CharClass = Fail | Pattern String | Error String is the same as "Maybe (Either Pattern Error)"? (Fail being a poorly chosen name - NoMatch would be better).
Yet, user-defined data types (no type synonyms!) may increase readability (and type safety). However, one disadvantage is that some type class instances have to be redefined (or derived) if needed.
Right. I prefer adding the Pattern & Error types, so they document which of Left and Right should be used here.
"Maybe" and "Either String" are fairly standard (and have Monad and what not instances), still your data type CharClass is perfect (if it serves the purpose). HTH Christian
Yup. Very educational. Thanks.
seen at http://pastebin.com/eyre8795 (as always, critiques welcome).
This has been updated to use the 'Maybe (Either Pattern Error)" return
values.
Thank you.

Am 07.04.2011 00:33, schrieb Mike Meyer:
Replacing Fail (or Nothing) by "Error String" is like going to "Either String String".
Ok this confuses me. What does "or Nothing" by "Error String" mean?
data CharClass = Fail | Pattern String | Error String
I assumed that you replaced "Fail" (which corresponds to "Nothing" for the Maybe data type) with "Error String" (without looking at your code), so that you can attach a message to a failure. data CharClass = Error String | Pattern String which is isomorphic to ""Either String String", where "Left" capture failure strings and "Right" captures valid patterns.
is the same as "Maybe (Either Pattern Error)"? (Fail being a poorly chosen name - NoMatch would be better).
I don't think, that you need "Maybe (Either Pattern Error)", just code the "Nothing" case as a suitable "error string". Also "Either Pattern Error" has the wrong order. The "Error" should be the "Left" component (by convention) for Monad instances to work as expected! (This also applies to your "Either Bool Error" usage.) The type synonyms Pattern and Error (being String) gain no additional type safety. They only reveal (to the reader) that Either was used in the wrong way, but would not stop you from using the Monad instances in a wrong way.
Yet, user-defined data types (no type synonyms!) may increase readability (and type safety). However, one disadvantage is that some type class instances have to be redefined (or derived) if needed.
Right. I prefer adding the Pattern& Error types, so they document which of Left and Right should be used here.
As I said, "Left" is by convention the error case.
"Maybe" and "Either String" are fairly standard (and have Monad and what not instances), still your data type CharClass is perfect (if it serves the purpose). HTH Christian
Yup. Very educational. Thanks.
seen at http://pastebin.com/eyre8795 (as always, critiques welcome).
This has been updated to use the 'Maybe (Either Pattern Error)" return values.
The CharClass as above would be safer. (Nesting of Either in Maybe is overkill.) Cheers Christian
participants (4)
-
Alex Rozenshteyn
-
Christian Maeder
-
MAN
-
Mike Meyer