Type input error while defining lexer using makeTokenParser
I have the following code:
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
However, when I try to import this file into ghci
, I get the following error:
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", ....]}
Note that this only happened after splitting the file; I previously had some code that was consuming "lexer" in the same module and then stripping it off.
What type annotations should I provide for this to work?
source to share
The solution is in the summary: def :: LanguageDef st
. This will fix from s0
to String
and m0
from Identity
.
Here's ... a free explanation.
Let's choose types from outside in. (This is not necessarily how the conclusion, but we usually have, or can get a top-level types of bindings.) Derived type of def
marked in the error message: GenLanguageDef s0 u m0
. Considering the definition GenLanguageDef
, the inferred type identStart
is therefore ParsecT s0 u m0 Char
.
letter
has a type Stream s m Char => ParsecT s u m Char
. Combining this with the type identStart
, we end up with a constraint Stream s0 m0 Char
that must be met in some way.
Alternatively, by providing a specific signature, I suggested simply fixing the variables s0
and m0
. Now the compiler can directly bind the class constraint since it knows what Identity
is Monad
and what an instance exists Monad m => Stream String m Char
.
(You might think that because it emptyDef
has a type LanguageDef st
that translates to GenLanguageDef String st Identity
, it def
will have that type. Ah, but you're using a record update syntax that allows type variables to change.)
source to share