In writing a parser for a particular computational biology file format, I’m running into some trouble.
Here’s my code:
betaLine = string "BETA " *> p_int <*> p_int <*> p_int <*> p_int <*> p_direction <*> p_exposure <* eol
p_int = liftA (read :: String -> Int) (many (char ' ') *> many1 digit <* many (char ' '))
p_direction = liftA mkDirection (many (char ' ') *> dir <* many (char ' '))
where dir = oneStringOf [ "1", "-1" ]
p_exposure = liftA (map mkExposure) (many (char ' ') *> many1 (oneOf "io") <* many (char ' '))
Now, if I comment out the definition for betaLine, everything compiles and I’ve successfully tested out the individual parsers p_int, p_direction, and p_exposure.
But, when that betaLine equation is present, I get a type error that I don’t quite understand. Is my understanding of the applicative <*> wrong? Ultimately, I want this to return Int -> Int -> Int -> Int -> Direction -> ExposureList, which I’ll then be able to give to the constructor for a BetaPair.
The type error is:
Couldn't match expected type `a5 -> a4 -> a3 -> a2 -> a1 -> a0'
with actual type `Int'
Expected type: Text.Parsec.Prim.ParsecT
s0 u0 m0 (a5 -> a4 -> a3 -> a2 -> a1 -> a0)
Actual type: Text.Parsec.Prim.ParsecT s0 u0 m0 Int
In the second argument of `(*>)', namely `p_int'
In the first argument of `(<*>)', namely `string "BETA " *> p_int'
tl;dr: you want this expression:
Read why below.
Once again, this is partly a precedence issue. What your current line does:
… is that it creates a parser like this:
This is not the main issue, though, and as a matter of fact, the semantically wrong parser above would still yield the correct result, if the rest of the parser were correct. However, as you say, you have a slight misunderstanding about how
<*>works. Its signature is:As you can see, the function should get a function wrapped in a functor as the first argument, which it then applies using the value wrapped in the functor in the second argument (thus the applicative functor). When you give it
p_intas the first argument at the beginning of your function, it is aParser Intand not aParser (a -> b), so the types don’t check.And as a matter of fact, they cannot be made to type check if the goal is what you stated with your reasoning; you want
betaLineto be aParser (Int -> Int -> Int -> Int -> Direction -> ExposureList), but how would that help you? You get a function that takes 4Ints, aDirectionandExposureList, and when you give that function to the constructor of aBetaPair, it is magically supposed to construct aBetaPairout of it? Remember that functions associate to the right, so if theBetaPairconstructor has type:… it doesn’t mean the same thing as:
It actually means this:
You can instead make the
betaLinebe aParser BetaPair, which would make more sense. You can use the<$>operator, which is a synonym forfmap(under the function arrow), which lets you lift yourBetaPairconstructor into theParserfunctor, and then apply individual arguments to it using the applicative functor interface. The<$>function has this type:In this case, your first argument that you’re lifting is the
BetaPairconstructor, which transforms the typesaandbinto the type components of theBetaPair“function”, yielding this specific signature:As you can see, what the
<$>will do here is to take a function as the left argument, and a value wrapped in a functor as the right argument, and apply the wrapped argument to the function.As a simpler example, if you have
f :: Int -> String, the following expression:… will parse an integer, apply the function
fwith that integer as the argument, and wrap the result in the functor, so the expression above has typeParser String. The type of<$>in this position is:So, using
<$>applies the first argument to your constructor. So how do you deal with the other arguments? Well, this is where the<*>comes in, and I think that you understand from the type signature what it does: if you chain its use, it will successively apply one more argument to the function wrapped in the functor to the left, by unwrapping the functor to the right. So, for a simpler example again; say that you have a functiong :: Int -> Int -> Stringand the following expression:The
g <$> p_intexpression will apply the result ofp_intto the first argument ofg, so the type of that expression isParser (Int -> String). The<*>then applies the next argument, with the specific type of<*>being:So, the type of the whole expression above is
Parser String.Equivalently, for your situation, you can let
BetaPairbe yourgin this case, yielding this pattern:As mentioned above, the resulting parser is thus: