parsec类型的问题

Noa*_*els 4 haskell parsec

在编写特定计算生物学文件格式的解析器时,我遇到了一些麻烦.

这是我的代码:

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 ' '))
Run Code Online (Sandbox Code Playgroud)

现在,如果我注释掉betaLine的定义,那么所有内容都会编译,并且我已成功测试了各个解析器p_int,p_direction和p_exposure.

但是,当存在betaLine等式时,我得到一个我不太明白的类型错误.我对应用<*>的理解是错误的吗?最后,我希望这返回Int - > Int - > Int - > Int - > Direction - > ExposureList,然后我将能够为BetaPair的构造函数提供.

类型错误是:

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'
Run Code Online (Sandbox Code Playgroud)

dfl*_*str 5

tl; dr:你想要这个表达式:

betaLine = string "BETA " *> (BetaPair <$> p_int <*> p_int  <*> p_int <*> p_int <*> p_direction <*> p_exposure) <* eol
Run Code Online (Sandbox Code Playgroud)

阅读以下原因.


再一次,这部分是优先问题.您当前的行是做什么的:

string "BETA " *> p_int <*> p_int ...
Run Code Online (Sandbox Code Playgroud)

...是它创建一个像这样的解析器:

(string "BETA " *> p_int) <*> (p_int) ...
Run Code Online (Sandbox Code Playgroud)

这不是主要问题,事实上,如果解析器的其余部分是正确的,那么上面的语义错误的解析器仍然会产生正确的结果.但是,正如你所说,你对如何<*>运作有一点误解.它的签名是:

(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,功能应该得到包裹在一个仿函数作为第一个参数,它则功能适用于使用包裹在仿函数的第二个参数(因此价值应用性仿函数).当你把它p_int作为函数开头的第一个参数时,它是a Parser Int而不是a Parser (a -> b),所以类型不检查.

事实上,如果目标是你用你的推理所说的那样,他们就不能进行打字检查; 你想betaLine成为一个Parser (Int -> Int -> Int -> Int -> Direction -> ExposureList),但这对你有什么帮助?你得到一个需要4 Int秒的函数,a DirectionExposureList,当你把这个函数BetaPair赋给a的构造函数时,神奇地认为它构造了一个BetaPair吗?请记住,函数关联到右侧,因此如果BetaPair构造函数具有类型:

Int -> Int -> Int -> Int -> Direction -> ExposureList -> BetaPair
Run Code Online (Sandbox Code Playgroud)

......这并不意味着:

(Int -> Int -> Int -> Int -> Direction -> ExposureList) -> BetaPair
Run Code Online (Sandbox Code Playgroud)

它实际上意味着:

Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair)))))
Run Code Online (Sandbox Code Playgroud)

你可以改为betaLine成为a Parser BetaPair,这会更有意义.您可以使用<$>运算符,它是fmap(在函数箭头下)的同义词,它允许您将BetaPair构造Parser函数提升到仿函数,然后使用applicative functor接口将单个参数应用于它.该<$>函数具有以下类型:

(<$>) :: Functor f => (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你抬起你的第一个参数是BetaPair构造函数,变换类型ab进入的类型组件BetaPair"功能",这产生特定的签名:

(<$>) :: (Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair)))))) 
      -> f Int -> f (Int -> (Int -> (Direction -> (ExposureList -> BetaPair))))
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,<$>这里的意思是将函数作为左参数,将函数包装在函数中作为右参数,并将包装参数应用于函数.

作为一个更简单的示例,如果您有f :: Int -> String,则使用以下表达式:

f <$> p_int
Run Code Online (Sandbox Code Playgroud)

...将解析一个整数,f将该整数作为参数应用于函数,并将结果包装在仿函数中,因此上面的表达式具有类型Parser String.<$>这个职位的类型是:

(<$>) :: (Int -> String) -> Parser Int -> Parser String
Run Code Online (Sandbox Code Playgroud)

因此,使用<$>第一个参数应用于构造函数.那么你如何处理其他论点呢?好吧,这就是它的<*>用武之地,我认为你从类型签名中理解它的作用:如果你链接它的用途,它将通过展开左边的functor中包含的函数连续再应用一个参数.向右的仿函数.所以,再一个简单的例子; 说你有一个函数g :: Int -> Int -> String和以下表达式:

g <$> p_int <*> p_int
Run Code Online (Sandbox Code Playgroud)

g <$> p_int表达将应用结果的p_int到的第一个参数g,从而使表达的类型是Parser (Int -> String).在<*>随后应用于下一个参数,与特定类型的<*>存在:

(<*>) :: Parser (Int -> String) -> Parser Int -> Parser String
Run Code Online (Sandbox Code Playgroud)

所以,上面整个表达式的类型是Parser String.

同样地,对于你的情况,你可以在这种情况下BetaPair成为你的g,产生这种模式:

BetaPair <$> one <*> parser <*> per <*> argument <*> to <*> betaPair
Run Code Online (Sandbox Code Playgroud)

如上所述,生成的解析器因此是:

betaLine = string "BETA " *> (BetaPair <$> p_int <*> p_int  <*> p_int <*> p_int <*> p_direction <*> p_exposure) <* eol
Run Code Online (Sandbox Code Playgroud)