haskell - 秒差距类型的问题

标签 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 ' '))

现在,如果我注释掉 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'

最佳答案

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

正如您所看到的,该函数应该获取一个封装在仿函数中的函数作为第一个参数,然后使用第二个参数中封装在仿函数中的值来应用该函数(因此 >应用仿函数)。当你给它 p_int作为函数开头的第一个参数,它是 Parser Int而不是Parser (a -> b) ,因此不检查类型。

事实上,他们无法检查目标是否符合你的推理;你想要betaLine成为Parser (Int -> Int -> Int -> Int -> Direction -> ExposureList) ,但这对你有什么帮助呢?你得到一个需要 4 Int 的函数s,a DirectionExposureList ,并且当您将该函数赋予 BetaPair 的构造函数时,它神奇地应该构建一个 BetaPair出来了吗?请记住,函数与右侧关联,因此如果 BetaPair构造函数的类型为:

Int -> Int -> Int -> Int -> Direction -> ExposureList -> BetaPair

...它与以下含义不同:

(Int -> Int -> Int -> Int -> Direction -> ExposureList) -> BetaPair

这实际上意味着:

Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair)))))

您可以改为 betaLineParser BetaPair ,这会更有意义。您可以使用 <$>运算符,它是 fmap 的同义词(在功能箭头下方),它可以让您抬起 BetaPair构造函数进入 Parser仿函数,然后使用应用仿函数接口(interface)将各个参数应用于它。 <$>函数有这种类型:

(<$>) :: Functor f => (a -> b) -> f a -> f b

在这种情况下,您要解除的第一个参数是 BetaPair构造函数,用于转换类型 ab进入 BetaPair 的类型组件“函数”,产生这个特定的签名:

(<$>) :: (Int -> (Int -> (Int -> (Int -> (Direction -> (ExposureList -> BetaPair)))))) 
      -> f Int -> f (Int -> (Int -> (Direction -> (ExposureList -> BetaPair))))

如您所见,<$> 是什么?这里要做的是将函数作为左参数,将包装在仿函数中的值作为右参数,并将包装的参数应用于函数。

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

f <$> p_int

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

(<$>) :: (Int -> String) -> Parser Int -> Parser String

所以,使用<$>将第一个参数应用于构造函数。那么你如何处理其他争论呢?嗯,这就是<*>的地方进来,我认为您从类型签名中了解了它的作用:如果您链接它的使用,它会通过将仿函数展开到右侧,依次将另一个参数应用到左侧仿函数中包装的函数。那么,再举一个更简单的例子;假设你有一个函数 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

关于haskell - 秒差距类型的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10726708/

相关文章:

function - 为什么返回函数叫return?

html - Lucid的 "Term"类型的真正含义

解析 Haskell 自定义数据类型

windows - Haskell Parsec 编译错误

regex - Attoparsec 解析失败但不应该有适当的回溯

Haskell - Parsec::解析空格直到字符串文字

haskell - forkIO/forkOS 和 forkProcess 之间的区别?

haskell - Haskell 中的类型 `Fix` 和函数 `fix` 如何相同?

windows - 如何在我的 TCP 服务器中获得所需的行为?

haskell - 将 Read 实例添加到 Path to Path 包