4

我正在玩 Parsec,我想将两个解析器组合成一个,并将结果放在一对中,然后将另一个函数提供给它以对解析结果进行操作以编写如下内容:

try (pFactor <&> (char '*' *> pTerm) `using` (*))

所以我写了这个:

(<&>) :: (Monad m) => m a -> m b -> m (a, b)
pa <&> pb = do
  a <- pa
  b <- pb
  return (a, b)

using :: (Functor f) => f (a, b) -> (a -> b -> c) -> f c
p `using` f = (uncurry f) <$> p

有没有类似于 (<&>) 的东西已经在某处实现了?或者这可以写成无意义的吗?我试过fmap (,)了,但似乎很难匹配类型。

4

5 回答 5

11

<&>liftM2将会更好

(,) <$> a <*> b

因为 Applicative 风格似乎越来越受欢迎并且非常简洁。对这样的事情使用应用风格将消除对<&>自身的需求,因为它比(,) <$> a <*> b.

此外,这甚至不需要 monad - 它也适用于 Applicatives。

于 2011-09-24T20:13:37.890 回答
7

有没有类似于 (<&>) 的东西已经在某处实现了?或者这可以随意写吗?我试过 fmap (,) 但似乎很难匹配类型。

如果它在任何地方实施,我现在不知道,但<&>应该与liftM2 (,). 不同之处fmap在于,它将二进制liftM2函数提升到 monad 中。

于 2011-09-24T20:10:05.093 回答
6

使用 applicative 风格,不需要为了立即应用 uncurried 函数而将中间结果放入元组中。只需使用“直接”应用函数<$>and <*>

try ((*) <$> pFactor <*> (char '*' *> pTerm))

一般来说,假设 和 的合理实例MonadApplicative

do x0 <- m0
   x1 <- m1
   ...
   return $ f x0 x1 ...

相当于

f <$> m0 <*> m1 <*> ...

除了后一种形式更通用并且只需要一个Applicative实例。(所有的单子也应该是应用函子,尽管语言没有强制要求)。

于 2011-09-24T20:20:46.353 回答
3

请注意,如果您从相反的方向走,Applicative您会发现您想要组合解析器的方式非常适合Arrow范式和Arrow 解析器的实现。例如:

import Control.Arrow

(<&>) = (&&&) 

p `using` f = p >>^ uncurry f 
于 2011-09-25T05:25:30.267 回答
1

是的,您可以使用应用风格,但我认为这不能回答您的任何一个问题。

是否有一些已经定义的组合器接受两个任意的 monad 并将它们的值类型放在一对中,然后将该对放在上下文中?

不在任何标准包中。

你能做到最后一点吗?

我不确定是否有一种方法可以使 arity 大于 1 点的 curried 函数免费。我不认为有。

希望能回答你的问题。

于 2011-09-24T20:38:05.907 回答