Reputation: 1744
Consider the following faulty parser written as StateT
.
type Parser a = StateT String Maybe a -- Maybe is chosen arbitrarily here.
oneDigit :: Parser Int
oneDigit = do
(x : _) <- get
return (read x :: Int)
It throws following error:
No instance for (MonadState [String] (StateT String Maybe))
However, that didn't happen when I change to say, oneChar :: Parser Char
.
My conclusion is that you can only define a Parser a
if your State is m a
Now my question is, how do I loose this coupling requirement?
Upvotes: 1
Views: 244
Reputation: 24832
The type error is trying to tell you that your state needs to be a list of strings instead of a single string, because you use
(x : _) <- get
to get the first element of the list and then read it with read
(which expects a String
).
Since you only want to parse a single digit, you can change the function to
oneDigit :: Parser Int
oneDigit = do
(x : _) <- get
return (read [x] :: Int)
[x]
creates a string from the single character (since String
is just an alias for [Char]
).
Note that you probably don't want to use read
in a parser implementation because it will crash with invalid input. reads
is a slightly better alternative that lets you handle error cases. For example:
oneDigit :: Parser Int
oneDigit = do
(x : xs) <- get
case reads [x] of
[(n, "")] -> put xs >> return n
_ -> lift Nothing
This also put
s the rest of the string back to the state so that your parser actually consumes input when it succeeds.
Upvotes: 3
Reputation: 3226
Your state is a String
, if you do (x : _) <- get
then x
is a Char
. You can't use read
on a Char
. The error printed is due to the type inference that works the other way around: from read x
the compiler infers that x
is a String
and that means that your state is [String]
, which is not true for Parser a
. Specifically, the errors says that (StateT String Maybe)
is not an instance of MonadState [String]
, that is true because it is an instance of MonadState String
. You can see it from GHCI:
> import Control.Monad.State
> :i StateT
...
instance Monad m => MonadState s (StateT s m)
Where s == String
in your case.
Now, if you want to read the next Char
in your string as a digit (a num from 0 to 9), you need to replace read
with digitToInt
from Data.Char
:
oneDigit :: Parser Int
oneDigit = do
(x : _) <- get -- x will be inferred as Char
return (digitToInt x :: Int)
Upvotes: 6