为什么这会循环使用'数据'但不是' newtype'?

时间:2017-01-15 02:34:43

标签: haskell

以下是代码:

import Control.Applicative

-- newtype Parser a = Parser { runParser :: String -> [(a, String)] }
data Parser a = Parser { runParser :: String -> [(a, String)] }

instance Functor Parser where
  fmap f (Parser p) = Parser (\s -> [(f x, s') | (x, s') <- p s ] )

instance Applicative Parser where
  pure a = Parser (\s -> [(a, s)])
  Parser q <*> Parser p = Parser (\s -> [(f x, s'') | (f, s') <- q s, (x, s'') <- p s'])

instance Alternative Parser where
  empty = Parser (\s -> [])
  Parser q <|> Parser p = Parser (\s -> q s ++ p s)

item = Parser (\s -> case s of
                  (x:xs) -> [(x, xs)]
                  _ -> []
              )

使用当前代码,runParser (some item) "abcd"循环,但如果Parser声明为newtype,则它可以正常工作。

1 个答案:

答案 0 :(得分:6)

这是获得the difference between data and newtype之一的好方法。这里问题的核心实际上是<|>定义的模式匹配。

instance Alternative Parser where
  empty = Parser (\s -> [])
  Parser q <|> Parser p = Parser (\s -> q s ++ p s)

请记住,在运行时,newtype与包装的类型相同。然后,当模式匹配newtype时,GHC不做任何事情 - 没有构造函数可以评估为WNHF。

相反,当data匹配时,看到模式Parser q告诉GHC它需要将该解析器评估为WNHF。这是一个问题,因为some<|>的无限折叠。使用data解决问题有两种方法:

  • Parser中没有<|>个模式:

    instance Alternative Parser where
      empty = Parser (\s -> [])
      q <|> p = Parser (\s -> runParser q s ++ runParser p s)
    
  • 使用lazy patterns

    instance Alternative Parser where
      empty = Parser (\s -> [])
      ~(Parser q) <|> ~(Parser p) = Parser (\s -> q s ++ p s)