如何在Parsec中使用Control.Monad.State? [英] How to use Control.Monad.State with Parsec?

查看:105
本文介绍了如何在Parsec中使用Control.Monad.State?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

<>我很惊讶,我无法找到任何信息。我必须是唯一遇到麻烦的人。



所以,假设我有一个破折号计数器。我希望它能够计算字符串中破折号的数量,并返回字符串。假装我给出了一个使用parsec的状态处理不起作用的例子。所以这应该工作:

  dashCounter = do 
str< - many1破折号
count< - 得到
return(count,str)


dash = do
char' - '
modify(+1)

事实上,这个编译。好的,所以我尝试使用它:

 :t解析dashCounter----
解析dashCounter----
::(Control.Monad.State.Class.MonadState
t Data.Functor.Identity.Identity,
Num t)=>
ParseError(t,[Char])

好的,这是有道理的。它应该返回状态和字符串。

 > parse dashCounter----

< interactive> :1:7:
没有用于(Control.Monad.State.Class.MonadState
t0 Data.Functor.Identity.Identity)的实例
使用`dashCounter'
可能的修正:

添加一个实例声明(Control.Monad.State.Class.MonadState
t0 Data.Functor.Identity.Identity)
在第一个参数` parse',即`dashCounter'
在表达式中:parse dashCounter----
在`it'的等式中:it = parse dashCounter----

糟糕。但是,那么它怎么可能有希望首先工作呢?没有办法输入初始状态。



还有一个函数:

 > runPT dashCounter(0 :: Int)----





 < interactive>:1:7:
没有实例用于(Control .Monad.State.Class.MonadState Int m0)使用`dashCounter'
引起的
可能的修正:

添加一个实例声明(Control.Monad.State。 Class.MonadState Int m0)
在`runPT'的第一个参数中,即`dashCounter'
在表达式中:runPT dashCounter(0 :: Int)----
在'it'的等式中:
it = runPT dashCounter(0 :: Int)----

我觉得我应该在其上运行状态,或者应该有一个已经在内部执行的功能,但我似乎无法弄清楚从哪里开始。 p>

编辑:我应该指定更多清除实际上,我不想使用parsec的状态处理。原因是我有一种感觉,我不希望它的回溯影响它收集的问题,我准备解决它。



然而,先生。 McCann已经弄清楚这应该如何组合在一起,最终的代码如下所示:

  dashCounter = do 
str < - many1 dash
count< - get
return(count,str)

dash = do
c< - char' - '
修改(+1)
返回c

test = runState(runPT dashCounter()----------)0
解决方案

>你实际上遇到了很多问题,所有这些都是第一次相对不明显。



从最简单的开始: dash 正在返回(),这看起来并不是你想要的,因为你正在收集结果。你可能想要像 dash = char' - '< * modify(+1)之类的东西。 (请注意,我在这里使用了一个来自 Control.Applicative 的操作符,因为它看起来比较整洁)。

接下来,清理一个混乱点:当你在GHCi中获得合理的类型签名时,注意的上下文(Control.Monad.State.Class.MonadState t Data.Functor.Identity.Identity,Num t )。这并不是说什么 ,而是告诉你他们需要 。没有什么能保证它所要求的实例存在,实际上它们不存在。 身份不是一个状态monad!



另一方面,你完全正确地认为 parse 没有意义;你不能在这里使用它。考虑它的类型: Stream s Identity t => Parsec s()a - > SourceName - > s - > ParseError a 。按照monad变换器惯例, Parsec 是应用于身份monad的 ParsecT 的同义词。虽然 ParsecT 确实提供了用户状态,但您显然不想使用它,并且 ParsecT does 而不是无论如何给出一个 MonadState 的实例。以下是唯一相关的实例: MonadState s m => MonadState s(ParsecT s'u m)。换句话说,要将解析器作为状态monad处理,必须将 ParsecT 应用于其他状态monad。



这种情况将我们带入下一个问题:模棱两可。你使用了很多类型方法,没有类型签名,所以你可能会遇到GHC无法知道你真正想要什么类型的情况,所以你必须告诉它。



现在,作为一个快速解决方案,我们首先定义一个类型的同义词,为我们想要的monad变换器堆栈命名:

  type StateParse a = ParsecT String()(StateT Int Identity)a 

dashCounter 相关类型签名:

  dashCounter :: StateParse(Int ,字符串)
dashCounter = do str< - many1 dash
count< - get
return(count,str)

并添加一个特殊用途的run函数:

$ p $ runStateParse现在,在GHCi中,我们可以使用一个或多个运算符来执行一个或多个运算符, :


  Main> runStateParse dashCounter---0 
(Right(3,---),3)

另外,请注意,在变换器堆栈周围使用 newtype 而不是类型同义词很常见。这可以帮助解决某些情况下的歧义问题,并且显然可以避免以巨大的类型签名结束。


I'm surprised that I could not find any info on this. I must be the only person having any trouble with it.

So, let's say I have a dash counter. I want it to count the number of dashes in the string, and return the string. Pretend I gave an example that won't work using parsec's state handling. So this should work:

dashCounter = do
  str <- many1 dash
  count <- get
  return (count,str)


dash = do
  char '-'
  modify (+1)

And indeed, this compiles. Okay, so I try to use it:

:t parse dashCounter "" "----"
parse dashCounter "" "----"
  :: (Control.Monad.State.Class.MonadState
        t Data.Functor.Identity.Identity,
      Num t) =>
     Either ParseError (t, [Char])

Okay, that makes sense. It should return the state and the string. Cool.

>parse dashCounter "" "----"

<interactive>:1:7:
    No instance for (Control.Monad.State.Class.MonadState
                       t0 Data.Functor.Identity.Identity)
      arising from a use of `dashCounter'
    Possible fix:
      add an instance declaration for
      (Control.Monad.State.Class.MonadState
         t0 Data.Functor.Identity.Identity)
    In the first argument of `parse', namely `dashCounter'
    In the expression: parse dashCounter "" "----"
    In an equation for `it': it = parse dashCounter "" "----"

Oops. But then how could it have ever hoped to work in the first place? There's no way to input the initial state.

There is also a function:

>runPT dashCounter (0::Int) "" "----"

But it gives a similar error.

<interactive>:1:7:
    No instance for (Control.Monad.State.Class.MonadState Int m0)
      arising from a use of `dashCounter'
    Possible fix:
      add an instance declaration for
      (Control.Monad.State.Class.MonadState Int m0)
    In the first argument of `runPT', namely `dashCounter'
    In the expression: runPT dashCounter (0 :: Int) "" "----"
    In an equation for `it':
        it = runPT dashCounter (0 :: Int) "" "----"

I feel like I should have to runState on it, or there should be a function that already does it internally, but I can't seem to figure out where to go from here.

Edit: I should have specified more clearly, I did not want to use parsec's state handling. The reason is I have a feeling I don't want its backtracking to affect what it collects with the problem I'm preparing to solve it with.

However, Mr. McCann has figured out how this should fit together and the final code would look like this:

dashCounter = do
  str <- many1 dash
  count <- get
  return (count,str)

dash = do
  c <- char '-'
  modify (+1)
  return c

test = runState (runPT dashCounter () "" "----------") 0

Thanks a lot.

解决方案

You've actually got multiple problems going on here, all of which are relatively non-obvious the first time around.

Starting with the simplest: dash is returning (), which doesn't seem to be what you want given that you're collecting the results. You probably wanted something like dash = char '-' <* modify (+1). (Note that I'm using an operator from Control.Applicative here, because it looks tidier)

Next, clearing up a point of confusion: When you get the reasonable-looking type signature in GHCi, note the context of (Control.Monad.State.Class.MonadState t Data.Functor.Identity.Identity, Num t). That's not saying what things are, it's telling you want they need to be. Nothing guarantees that the instances it's asking for exist and, in fact, they don't. Identity is not a state monad!

On the other hand, you're absolutely correct in thinking that parse doesn't make sense; you can't use it here. Consider its type: Stream s Identity t => Parsec s () a -> SourceName -> s -> Either ParseError a. As is customary with monad transformers, Parsec is an synonym for ParsecT applied to the identity monad. And while ParsecT does provide user state, you apparently don't want to use it, and ParsecT does not give an instance of MonadState anyhow. Here's the only relevant instance: MonadState s m => MonadState s (ParsecT s' u m). In other words, to treat a parser as a state monad you have to apply ParsecT to some other state monad.

This sort of brings us to the next problem: Ambiguity. You're using a lot of type class methods and no type signatures, so you're likely to run into situations where GHC can't know what type you actually want, so you have to tell it.

Now, as a quick solution, let's first define a type synonym to give a name to the monad transformer stack we want:

type StateParse a = ParsecT String () (StateT Int Identity) a

Give dashCounter the relevant type signature:

dashCounter :: StateParse (Int, String)
dashCounter = do str <- many1 dash
                 count <- get
                 return (count,str)

And add a special-purpose "run" function:

runStateParse p sn inp count = runIdentity $ runStateT (runPT p () sn inp) count

Now, in GHCi:

Main> runStateParse dashCounter "" "---" 0
(Right (3,"---"),3)

Also, note that it's pretty common to use a newtype around a transformer stack instead of just a type synonym. This can help with the ambiguity issues in some cases, and obviously avoids ending up with gigantic type signatures.

这篇关于如何在Parsec中使用Control.Monad.State?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆