tl;博士:你想要这个表达:
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
如您所见,该函数应该获得一个包装在函子中的函数作为第一个参数,然后使用包装在第二个参数中的函子中的值应用该函数(因此是应用函子)。当您p_int
在函数开头将其作为第一个参数时,它是 aParser Int
而不是 a Parser (a -> b)
,因此不会检查类型。
事实上,它们不能用来检查目标是否是你用推理陈述的;你想betaLine
成为一个Parser (Int -> Int -> Int -> Int -> Direction -> ExposureList)
,但这对你有什么帮助?你得到一个需要 4 Int
s, a Direction
and的函数ExposureList
,当你将该函数提供给 a 的构造函数时BetaPair
,它应该神奇地构造 aBetaPair
出来吗?请记住,函数关联到右侧,因此如果BetaPair
构造函数具有类型:
Int -> Int -> Int -> Int -> Direction -> ExposureList -> BetaPair
...这与以下内容不同:
(Int -> Int -> Int -> Int -> Direction -> ExposureList) -> BetaPair
它实际上是这个意思:
Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair)))))
您可以改为将其betaLine
设为 a Parser BetaPair
,这样会更有意义。您可以使用<$>
运算符,它是fmap
(在函数箭头下)的同义词,它允许您将BetaPair
构造函数提升到Parser
函子,然后使用应用函子接口将单个参数应用于它。该<$>
函数具有以下类型:
(<$>) :: Functor f => (a -> b) -> f a -> f b
在这种情况下,您要提升的第一个参数是BetaPair
构造函数,它将类型转换为“函数”的类型组件a
,产生这个特定的签名:b
BetaPair
(<$>) :: (Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair))))))
-> f Int -> f (Int -> (Int -> (Direction -> (ExposureList -> BetaPair))))
如您所见,<$>
这里将做的是将一个函数作为左参数,并将一个包装在仿函数中的值作为右参数,并将包装的参数应用于函数。
作为一个更简单的示例,如果您有f :: Int -> String
,则以下表达式:
f <$> p_int
... 将解析一个整数,f
将该整数作为参数应用函数,并将结果包装在函子中,因此上面的表达式具有 type Parser String
。<$>
在这个位置的类型是:
(<$>) :: (Int -> String) -> Parser Int -> Parser String
因此, using<$>
将第一个参数应用于您的构造函数。那么你如何处理其他争论呢?好吧,这就是<*>
in 的用武之地,我认为您从类型签名中了解了它的作用:如果您将其使用链接起来,它将通过展开右边的函子。所以,再举一个更简单的例子;假设您有一个函数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