Parsing 如何判断Parsec解析器是否在Haskell中使用常量堆空间

Parsing 如何判断Parsec解析器是否在Haskell中使用常量堆空间,parsing,haskell,heap-memory,tail-recursion,parsec,Parsing,Haskell,Heap Memory,Tail Recursion,Parsec,在一次采访中,我问了以下问题 解析器: 这是一个显著的进步,但我不明白为什么 manyllengthcontheap使用常量堆空间,而我原来的manyllength不使用 如果将()内联到多个长度的,它看起来有点像这样: manyLengthInline :: forall s u m a. Monad m => ParsecT s u m a -> ParsecT s u m Int manyLengthInline p = go 0 where go :: Int

在一次采访中,我问了以下问题 解析器:

这是一个显著的进步,但我不明白为什么
manyllengthcontheap
使用常量堆空间,而我原来的
manyllength
不使用

如果将
()
内联到
多个长度的
,它看起来有点像这样:

manyLengthInline
  :: forall s u m a. Monad m => ParsecT s u m a -> ParsecT s u m Int
manyLengthInline p = go 0
  where
    go :: Int -> ParsecT s u m Int
    go !i =
      ParsecT $ \s cok cerr eok eerr ->
        let meerr :: ParserError -> m b
            meerr err =
              let neok :: Int -> State s u -> ParserError -> m b
                  neok y s' err' = eok y s' (mergeError err err')
                  neerr :: ParserError -> m b
                  neerr err' = eerr $ mergeError err err'
              in unParser (pure i) s cok cerr neok neerr
        in unParser (p *> go (i + 1)) s cok cerr eok meerr
manyLengthConstantHeapInline
  :: forall s u m a. Monad m => ParsecT s u m a -> ParsecT s u m Int
manyLengthConstantHeapInline p = go 0
  where
    go :: Int -> ParsecT s u m Int
    go !i =
      ParsecT $ \s cok cerr eok eerr ->
        let mcok :: Bool -> State s u -> ParserError -> m b
            mcok success s' err =
                let peok :: Int -> State s u -> ParserError -> m b
                    peok int s'' err' = cok int s'' (mergeError err err')
                    peerr :: ParserError -> m b
                    peerr err' = cerr (mergeError err err')
                in unParser
                    (if success then go (i + 1) else pure i)
                    s'
                    cok
                    cerr
                    peok
                    peerr
            meok :: Bool -> State s u -> ParserError -> m b
            meok success s' err =
                let peok :: Int -> State s u -> ParserError -> m b
                    peok int s'' err' = eok int s'' (mergeError err err')
                    peerr :: ParserError -> m b
                    peerr err' = eerr (mergeError err err')
                in unParser
                    (if success then go (i + 1) else pure i)
                    s'
                    cok
                    pcerr
                    peok
                    peerr
        in unParser ((p *> pure True) <|> pure False) s mcok cerr meok eerr
如果将
(>>=)
内联到
多个长度常量映射中,它看起来有点像这样:

manyLengthInline
  :: forall s u m a. Monad m => ParsecT s u m a -> ParsecT s u m Int
manyLengthInline p = go 0
  where
    go :: Int -> ParsecT s u m Int
    go !i =
      ParsecT $ \s cok cerr eok eerr ->
        let meerr :: ParserError -> m b
            meerr err =
              let neok :: Int -> State s u -> ParserError -> m b
                  neok y s' err' = eok y s' (mergeError err err')
                  neerr :: ParserError -> m b
                  neerr err' = eerr $ mergeError err err'
              in unParser (pure i) s cok cerr neok neerr
        in unParser (p *> go (i + 1)) s cok cerr eok meerr
manyLengthConstantHeapInline
  :: forall s u m a. Monad m => ParsecT s u m a -> ParsecT s u m Int
manyLengthConstantHeapInline p = go 0
  where
    go :: Int -> ParsecT s u m Int
    go !i =
      ParsecT $ \s cok cerr eok eerr ->
        let mcok :: Bool -> State s u -> ParserError -> m b
            mcok success s' err =
                let peok :: Int -> State s u -> ParserError -> m b
                    peok int s'' err' = cok int s'' (mergeError err err')
                    peerr :: ParserError -> m b
                    peerr err' = cerr (mergeError err err')
                in unParser
                    (if success then go (i + 1) else pure i)
                    s'
                    cok
                    cerr
                    peok
                    peerr
            meok :: Bool -> State s u -> ParserError -> m b
            meok success s' err =
                let peok :: Int -> State s u -> ParserError -> m b
                    peok int s'' err' = eok int s'' (mergeError err err')
                    peerr :: ParserError -> m b
                    peerr err' = eerr (mergeError err err')
                in unParser
                    (if success then go (i + 1) else pure i)
                    s'
                    cok
                    pcerr
                    peok
                    peerr
        in unParser ((p *> pure True) <|> pure False) s mcok cerr meok eerr

为什么
manylengthConstantTheap
在堆空间不变的情况下运行,而
manyllength
没有?它看起来不像是对
go
的递归调用
manyllength>ap
manyllength
的尾部调用位置

将来编写parsec解析器时,我如何知道空间 对给定解析器的需求?李耀霞怎么知道的
manylengconstant地图
可以吗

我觉得我对预测哪些解析器将使用 一个大的输入上有很多内存

有没有一种简单的方法来确定给定的函数是否 Haskell中的tail递归而不运行它?或者更好,不用编译
它?

尾部递归对于haskell代码在常量空间中运行来说不是必要的或足够的。@Carl这很有趣,谢谢。你能添加一个解释它的答案吗?或者给我指一篇文章或博客来解释它?
newtype ParsecT s u m a = ParsecT
  { unParser
      :: forall b .
         State s u
      -> (a -> State s u -> ParseError -> m b) -- consumed ok
      -> (ParseError -> m b)                   -- consumed err
      -> (a -> State s u -> ParseError -> m b) -- empty ok
      -> (ParseError -> m b)                   -- empty err
      -> m b
  }