使用makeTokenParser定义词法分析器时,类型推断失败

时间:2014-12-04 03:24:15

标签: haskell parsec

我有以下代码:

module Lexer (lexer) where                                                                                                                                                                                                
import Text.Parsec                                                                                                                                                                                                        
import Text.Parsec.Token                                                                                                                                                                                                  
import Text.Parsec.Language                                                                                                                                                                                               

opChars = "<>+=*-/!:"                                                                                                                                                                                                     

def = emptyDef {                                                                                                                                                                                                          
    commentStart = "/*"                                                                                                                                                                                                   
  , commentEnd = "*/"                                                                                                                                                                                                     
  , identStart = letter                                                                                                                                                                                                   
  , identLetter = alphaNum                                                                                                                                                                                                
  , opStart = oneOf opChars                                                                                                                                                                                               
  , opLetter = oneOf opChars                                                                                                                                                                                              
  , reservedOpNames = ["+", "-", "*", "!=", "==", ":=", "<", "<=", ">", ">="]                                                                                                                                             
  , reservedNames = ["and", "not", "or", "p_", "when", "output", "of"]                                                                                                                                                    
  }

lexer = makeTokenParser def                                                                                                                                                                                               

但是,当我尝试在ghci中导入此文件时,出现以下错误:

Prelude> :l Lexer.hs
[1 of 1] Compiling Lexer            ( Lexer.hs, interpreted )

Lexer.hs:11:18:
    No instance for (Stream s0 m0 Char) arising from a use of `letter'
    The type variables `s0', `m0' are ambiguous
    Relevant bindings include
      def :: GenLanguageDef s0 u m0 (bound at Lexer.hs:8:1)
    Note: there are several potential instances:
      instance Monad m =>
               Stream Data.ByteString.Internal.ByteString m Char
        -- Defined in `Text.Parsec.Prim'
      instance Monad m =>
               Stream Data.ByteString.Lazy.Internal.ByteString m Char
        -- Defined in `Text.Parsec.Prim'
      instance Monad m => Stream Data.Text.Internal.Lazy.Text m Char
        -- Defined in `Text.Parsec.Prim'
      ...plus two others
    In the `identStart' field of a record
    In the expression:
      emptyDef
        {commentStart = "/*", commentEnd = "*/", identStart = letter,
         identLetter = alphaNum, opStart = oneOf opChars,
         opLetter = oneOf opChars, reservedOpNames = ["+", "-", "*", ....],
         reservedNames = ["and", "not", "or", ....]}
    In an equation for `def':
        def
          = emptyDef
              {commentStart = "/*", commentEnd = "*/", identStart = letter,
               identLetter = alphaNum, opStart = oneOf opChars,
               opLetter = oneOf opChars, reservedOpNames = ["+", "-", ....],
               reservedNames = ["and", "not", ....]}

请注意,这仅在拆分文件后发生;我以前有一些代码消耗了&#34; lexer&#34;在同一模块中,然后将其分开。

我需要提供哪些类型的注释才能使其正常工作?

2 个答案:

答案 0 :(得分:3)

解决方案是注释:def :: LanguageDef st。这会将s0修改为String,将m0修复为Identity

这是一个......松散的解释。

让我们从外部选择类型。(这不一定是推理如何工作,但我们通常已经或者可以获得顶级绑定的类型。)def的推断类型在错误消息:GenLanguageDef s0 u m0。查看GenLanguageDef的定义,因此identStart的推断类型为ParsecT s0 u m0 Char

letter的类型为Stream s m Char => ParsecT s u m Char。使用identStart类型统一,我们得到需要以某种方式满足的约束Stream s0 m0 Char

monomorphism restriction禁止编译器简单地将推断的约束浮动到def类型。禁用限制后,def会推断出类型Stream s0 m0 Char => GenLanguageDef s0 u m0。消费者可以像在之前的单文件解决方案中那样修复类型变量。

或者,提供我建议的具体签名只需修复变量s0m0。现在编译器可以直接满足类约束,因为它知道IdentityMonad并且有一个实例Monad m => Stream String m Char

(您会认为,因为emptyDef的类型为LanguageDef st,转换为GenLanguageDef String st Identitydef将具有该类型。啊,但您正在使用记录更新语法,允许更改类型变量。)

答案 1 :(得分:2)

使用pragma:

{-# LANGUAGE NoMonomorphismRestriction #-}