2017-01-15 42 views
2

下面是代码:为什么这会循环'数据'而不是'新类型'?

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"循环,但如果分析器被声明为newtype,它工作得很好。

+1

究竟是什么你问?你有没有可以展示这个的例子? – Alec

+0

这个问题几乎没有任何信息。如果您不分享更多细节,我们无法猜测发生了什么。您应该提供[MCVE](http://stackoverflow.com/help/mcve)。 – chi

回答

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) 
    
+1

第一种严格的模式很好,因为'<|>'是左偏的。所以这可以写成“Parser q <|>〜(Parser p)= ...'。但在这种情况下,当然最好的解决方案就是使用'newtype'! – dfeuer

+0

谢谢,这有助于很多。尽管如此,我仍然不太理解被评估为WNHF的“数据”与关于“<|>”的问题之间的关系。 – Procrade

相关问题