I can never do too many Haskell tutorials. There is always something to refine and learn. So, I am working through chapter 10 of Real World Haskell, and everything makes sense.
but
Why do
newtype Parse a = Parse { runParse :: ParseState -> Either String (a, ParseState) }
identity :: a -> Parse a
identity a = Parse (s -> Right (a, s))
getState :: Parse ParseState
getState = Parse (s -> Right (s, s))
putState :: Parse ()
putState = Parse (s -> Right ((), s))
Why not simply do
type Parse a = ParseState -> Either String (a, ParseState)
identity :: a -> Parse a
identity a = s -> Right (a, s)
getState :: Parse ParseState
getState = s -> Right (s, s)
putState :: Parse ()
putState = s -> Right ((), s)
In other words, what is the purpose of the wrap and unwrap pattern in here (which I see used in a lot of different places in Haskell)? We get the same Parse Whatever
data type, but without the extra indirection.
The answer is that Haskell doesn’t have arbitrary type lambdas, so when we want to eventually make our Parse
type an instance of Monad
, a newtype
is required.
3
It looks like using the notation you’re taking issue with is a point of contention. From the comments on that Real World Haskell page:
I’m trying to go through the monad chapter now (chapter 14), and it
looks more and more like the parse example in this chapter is somewhat
contrived, in that some concepts it presents don’t make sense and lead
to what looks like needlessly hard-to-understand and complicated code…
and
While newtype and record syntax are explained in chapter 6, here (and
for the rest of the book) they’re not used for “records”, just for
what looks like a poor man’s substitute for encapsulation. I can’t
really see why. Isn’t the module system provided by Haskell enough for
this purpose?
In other words, the answer may well be “don’t”.
1