Haskell 如何在Parsec中使用Control.Monad.State?
我很惊讶,我找不到这方面的任何信息。我一定是唯一有麻烦的人了 那么,假设我有一个仪表盘计数器。我希望它计算字符串中破折号的数量,然后返回字符串。假设我给出了一个使用parsec的状态处理不起作用的示例。因此,这应该是可行的:Haskell 如何在Parsec中使用Control.Monad.State?,haskell,parsec,Haskell,Parsec,我很惊讶,我找不到这方面的任何信息。我一定是唯一有麻烦的人了 那么,假设我有一个仪表盘计数器。我希望它计算字符串中破折号的数量,然后返回字符串。假设我给出了一个使用parsec的状态处理不起作用的示例。因此,这应该是可行的: dashCounter = do str <- many1 dash count <- get return (count,str) dash = do char '-' modify (+1) 好吧,这是有道理的。它应该返回状态和字符
dashCounter = do
str <- many1 dash
count <- get
return (count,str)
dash = do
char '-'
modify (+1)
好吧,这是有道理的。它应该返回状态和字符串。酷
>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 "" "----"
但它给出了一个类似的错误
<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) "" "----"
: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)“”------
我觉得我应该在它上面运行state,或者应该有一个函数已经在内部运行了,但是我似乎不知道从这里可以走到哪里
编辑:我应该更清楚地指定,我不想使用parsec的状态处理。原因是我有一种感觉,我不想让它的回溯影响它收集到的东西,因为我正准备解决这个问题
然而,McCann先生已经弄明白了这应该如何结合在一起,最终的代码是这样的:
dashCounter = do
str <- many1 dash
count <- get
return (count,str)
dash = do
c <- char '-'
modify (+1)
return c
test = runState (runPT dashCounter () "" "----------") 0
dashCounter=do
str如果您想使用Parsec提供的用户状态组件作为内置功能,那么您可以使用getState
和modifyState
一元函数
虽然使用dash
的返回似乎没有什么用处,但我还是尽量忠实于您的示例程序
import Text.Parsec
dashCounter :: Parsec String Int (Int, [()])
dashCounter = do
str <- many1 dash
count <- getState
return (count,str)
dash :: Parsec String Int ()
dash = do
char '-'
modifyState (+1)
test = runP dashCounter 0 "" "---"
import Text.Parsec
dashCounter::Parsec字符串Int(Int,[()]))
dashCounter=do
str你实际上遇到了很多问题,所有这些问题在第一次出现时都相对不明显
从最简单的开始:dash
返回()
,这似乎不是您想要的,因为您正在收集结果。您可能需要类似于dash=char'-'parsecss()a->SourceName->s->ParseError a
。与monad transformers一样,Parsec
是应用于标识monad的ParsecT
的同义词。虽然ParsecT
确实提供了用户状态,但您显然不想使用它,而且ParsecT
无论如何都不会给出MonadState
的实例。这里是唯一相关的实例:MonadState s m=>MonadState s(ParsecT s'u m)
。换句话说,要将解析器视为状态单子,必须将ParsecT
应用于其他一些状态单子
这就给我们带来了下一个问题:模糊性。您使用了很多类型类方法,但没有类型签名,因此可能会遇到GHC不知道您实际需要的类型的情况,因此您必须告诉它
现在,作为一个快速解决方案,让我们首先定义一个类型同义词,为我们想要的monad transformer堆栈命名:
type StateParse a = ParsecT String () (StateT Int Identity) a
给dashCounter
相关的类型签名:
dashCounter :: StateParse (Int, String)
dashCounter = do str <- many1 dash
count <- get
return (count,str)
现在,在GHCi中:
Main> runStateParse dashCounter "" "---" 0
(Right (3,"---"),3)
另外,请注意,在转换器堆栈周围使用newtype
而不仅仅是类型同义词是非常常见的。在某些情况下,这有助于解决歧义问题,并明显避免以巨大的类型签名结束。虽然这些答案解决了这一特定问题,但它们通过这样的方法忽略了更严重的潜在问题。我想在这里为其他任何看到这个答案的人描述一下
用户状态和使用StateT转换器之间存在差异。内部用户状态在回溯时重置,但StateT未重置。考虑下面的代码。如果有破折号,我们想在计数器上加一个,如果有加号,我们想在计数器上加两个。它们产生不同的结果
可以看出,使用内部状态和连接StateT变压器都能提供正确的结果。后者的代价是必须明确地提升操作,并且对类型更加小心
import Text.Parsec hiding (State)
import Control.Monad.State
import Control.Monad.Identity
f :: ParsecT String Int Identity Int
f = do
try dash <|> plus
getState
dash = do
modifyState (+1)
char '-'
plus = do
modifyState (+2)
char '+'
f' :: ParsecT String () (State Int) ()
f' = void (try dash' <|> plus')
dash' = do
modify (+1)
char '-'
plus' = do
modify (+2)
char '+'
f'' :: StateT Int (Parsec String ()) ()
f'' = void (dash'' <|> plus'')
dash'' :: StateT Int (Parsec String ()) Char
dash'' = do
modify (+1)
lift $ char '-'
plus'' :: StateT Int (Parsec String ()) Char
plus'' = do
modify (+2)
lift $ char '+'
看看这个:
runStateParse p sn inp count = runIdentity $ runStateT (runPT p () sn inp) count
Main> runStateParse dashCounter "" "---" 0
(Right (3,"---"),3)
import Text.Parsec hiding (State)
import Control.Monad.State
import Control.Monad.Identity
f :: ParsecT String Int Identity Int
f = do
try dash <|> plus
getState
dash = do
modifyState (+1)
char '-'
plus = do
modifyState (+2)
char '+'
f' :: ParsecT String () (State Int) ()
f' = void (try dash' <|> plus')
dash' = do
modify (+1)
char '-'
plus' = do
modify (+2)
char '+'
f'' :: StateT Int (Parsec String ()) ()
f'' = void (dash'' <|> plus'')
dash'' :: StateT Int (Parsec String ()) Char
dash'' = do
modify (+1)
lift $ char '-'
plus'' :: StateT Int (Parsec String ()) Char
plus'' = do
modify (+2)
lift $ char '+'
*Main> runParser f 0 "" "+"
Right 2
*Main> flip runState 0 $ runPT f' () "" "+"
(Right (),3)
*Main> runParser (runStateT f'' 0) () "" "+"
Right ((),2)