tl; dr : you need this expression:
betaLine = string "BETA " *> (BetaPair <$> p_int <*> p_int <*> p_int <*> p_int <*> p_direction <*> p_exposure) <* eol
Read below.
Once again, this is partly a priority issue. What is your current line:
string "BETA " *> p_int <*> p_int ...
... is that it creates a parser as follows:
(string "BETA " *> p_int) <*> (p_int) ...
This is not the main problem, and, in fact, the semantically incorrect parser above will still give the correct result if the rest of the parser was correct. However, as you say, you have a slight misunderstanding about how <*>
works. His signature:
(<*>) :: Applicative f => f (a -> b) -> fa -> fb
As you can see, the function should get the function wrapped in the functor as the first argument, which then applies using the value wrapped in the functor in the second argument (thus applied ). When you give it p_int
as the first argument at the beginning of your function, it is Parser Int
, not Parser (a -> b)
, so the types are not checked.
And, in fact, they cannot be printed to check if the goal is what you stated with your reasoning; you want betaLine
be Parser (Int -> Int -> Int -> Int -> Direction -> ExposureList)
, but how does that help you? You get a function that takes 4 Int
s, a Direction
and ExposureList
, and when you pass this function to the BetaPair
constructor, is it magically supposed to build BetaPair
from it? Remember that functions are connected on the right, therefore, if the BetaPair
constructor is of type:
Int -> Int -> Int -> Int -> Direction -> ExposureList -> BetaPair
... this does not mean the same as:
(Int -> Int -> Int -> Int -> Direction -> ExposureList) -> BetaPair
This actually means:
Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair)))))
Instead, you can make betaLine
be Parser BetaPair
, which will make more sense. You can use the <$>
operator, which is synonymous with fmap
(under the function arrow), which allows you to raise the BetaPair
constructor into the Parser
functor, and then apply individual arguments to it using the applicative functional interface. The <$>
function has this type:
(<$>) :: Functor f => (a -> b) -> fa -> fb
In this case, the first argument you raise is the BetaPair
constructor, which converts the types a
and b
to components of the BetaPair
function BetaPair
", which gives this particular signature
(<$>) :: (Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair)))))) -> f Int -> f (Int -> (Int -> (Direction -> (ExposureList -> BetaPair))))
As you can see, here <$>
will use the function as the left argument, and the value included in the functor as the correct argument, and apply the wrapped argument to the function.
As a simpler example, if you have f :: Int -> String
, the following expression:
f <$> p_int
... will parse the integer, apply the function f
with that integer as an argument, and wrap the result in a functor, so the expression above is of type Parser String
. Type <$>
in this position:
(<$>) :: (Int -> String) -> Parser Int -> Parser String
Thus, using <$>
applies the first argument to your constructor. So how do you feel about the other arguments? Well, thatβs where <*>
comes in, and I think that you understand from a type signature what it does: if you bind its use, it will sequentially apply another argument to the function wrapped in the functor on the left, by expanding the functor to the right . So, for a simpler example again; let's say that you have the function g :: Int -> Int -> String
and the following expression:
g <$> p_int <*> p_int
In the expression g <$> p_int
, the result g <$> p_int
will be applied to the first argument of g
, so the type of this expression is Parser (Int -> String)
. Then <*>
applies the following argument, with the concrete type <*>
being:
(<*>) :: Parser (Int -> String) -> Parser Int -> Parser String
So, the type of the whole expression is above: Parser String
.
Equivalently, for your situation, you can let BetaPair
be your g
in this case, by getting this template:
BetaPair <$> one <*> parser <*> per <*> argument <*> to <*> betaPair
As mentioned above, the resulting parser is thus:
betaLine = string "BETA " *> (BetaPair <$> p_int <*> p_int <*> p_int <*> p_int <*> p_direction <*> p_exposure) <* eol