tl;dr : この式が必要です:
betaLine = string "BETA " *> (BetaPair <$> p_int <*> p_int <*> p_int <*> p_int <*> p_direction <*> p_exposure) <* eol
以下の理由をお読みください。
繰り返しますが、これは部分的に優先順位の問題です。あなたの現在の行は何をしますか:
string "BETA " *> p_int <*> p_int ...
... 次のようなパーサーを作成するということです。
(string "BETA " *> p_int) <*> (p_int) ...
ただし、これは主な問題ではありません。実際のところ、パーサーの残りの部分が正しければ、上記の意味的に間違ったパーサーでも正しい結果が得られます。ただし、おっしゃる通り、仕組みについて少し誤解があります<*>。その署名は次のとおりです。
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
ご覧のとおり、関数は最初の引数としてファンクターにラップされた関数を取得する必要があります。これは、2 番目の引数のファンクターにラップされた値を使用して適用されます (したがって、アプリカティブ ファンクター)。関数の最初の最初の引数としてそれを与えると、それは a ではなく aであるため、型はチェックされません。p_intParser IntParser (a -> b)
実際のところ、目標があなたの推論で述べたものであるかどうかを型チェックすることはできません。になりたいbetaLineのですがParser (Int -> Int -> Int -> Int -> Direction -> ExposureList)、それはどのように役立ちますか? 4 つIntの s、 aDirectionおよびを受け取る関数を取得し、ExposureListその関数を a のコンストラクターに渡すと、BetaPair魔法のようにそれから a を構築するBetaPairことになっていますか? 関数は右側に関連付けられることに注意してください。したがって、BetaPairコンストラクターに型がある場合:
Int -> Int -> Int -> Int -> Direction -> ExposureList -> BetaPair
... 次と同じ意味ではありません。
(Int -> Int -> Int -> Int -> Direction -> ExposureList) -> BetaPair
これは実際には次のことを意味します。
Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair)))))
代わりに を にすることができますbetaLine。Parser BetaPairこれはより理にかなっています。(関数矢印の下)<$>の同義語である演算子を使用すると、コンストラクターをファンクターに持ち上げてから、アプリケーション ファンクター インターフェイスを使用して個々の引数をそれに適用できます。関数には次のタイプがあります。fmapBetaPairParser<$>
(<$>) :: Functor f => (a -> b) -> f a -> f b
この場合、持ち上げる最初の引数はBetaPairコンストラクターであり、型を「関数」の型コンポーネントにa変換し、この特定の署名を生成します。bBetaPair
(<$>) :: (Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair))))))
-> f Int -> f (Int -> (Int -> (Direction -> (ExposureList -> BetaPair))))
ご覧のとおり、<$>ここで will が行うことは、左引数として関数を取り、右引数としてファンクターにラップされた値を取り、ラップされた引数を関数に適用することです。
より簡単な例として、f :: Int -> String次の式がある場合:
f <$> p_int
... は整数を解析し、fその整数を引数として関数を適用し、結果をファンクターでラップするため、上記の式は type になりParser Stringます。<$>この位置の のタイプは次のとおりです。
(<$>) :: (Int -> String) -> Parser Int -> Parser String
したがって、using<$>は最初の引数をコンストラクターに適用します。では、他の議論をどのように処理しますか? さて、これが の<*>出番であり、型シグネチャからそれが何をするかを理解していると思います: その使用を連鎖させると、左側のファンクタにラップされた関数にもう 1 つの引数を連続して適用します。ファンクタを右に。したがって、もう一度簡単な例を示します。関数g :: Int -> Int -> Stringと次の式があるとします。
g <$> p_int <*> p_int
式はのg <$> p_int結果p_intを の最初の引数に適用するgため、その式の型は ですParser (Int -> String)。次に<*>、特定のタイプの次の引数を適用します<*>。
(<*>) :: Parser (Int -> String) -> Parser Int -> Parser String
したがって、上記の式全体の型は ですParser String。
同様に、あなたの状況では、この場合はBetaPairあなたになることができ、次のパターンが得られます。g
BetaPair <$> one <*> parser <*> per <*> argument <*> to <*> betaPair
前述のように、結果のパーサーは次のようになります。
betaLine = string "BETA " *> (BetaPair <$> p_int <*> p_int <*> p_int <*> p_int <*> p_direction <*> p_exposure) <* eol