The following code fails with an "ambiguous type variable" error (bottom). However, an alternate definition which updates the record twice works just fine: why is this? Further, the "broken" definition below is very similar to the one in the Trifecta source. I'm compiling with GHC 7.10.3 against trifecta 1.5.2 and parsers 0.12.3.
module Main where
import Text.Trifecta
import Text.Parser.Token.Style as T
-- This definition causes a type error
identStyle :: TokenParsing m => IdentifierStyle m
identStyle =
T.emptyIdents
{ _styleStart = letter
, _styleLetter = letter
}
Here is the working (alternative) definition
identStyle :: TokenParsing m => IdentifierStyle m
identStyle = T.emptyIdents { _styleStart = letter } { _styleLetter = letter }
The error generated by the first definition is:
Main.hs:10:3:
Could not deduce (TokenParsing t0)
arising from a use of ‘emptyIdents’
from the context (TokenParsing m)
bound by the type signature for
identStyle :: TokenParsing m => IdentifierStyle m
at Main.hs:8:15-49
The type variable ‘t0’ is ambiguous
Note: there are several potential instances:
instance attoparsec-0.13.0.1:Data.Attoparsec.Internal.Types.Chunk
t =>
TokenParsing
(attoparsec-0.13.0.1:Data.Attoparsec.Internal.Types.Parser t)
-- Defined in ‘Text.Parser.Token’
instance TokenParsing Text.ParserCombinators.ReadP.ReadP
-- Defined in ‘Text.Parser.Token’
instance TokenParsing m => TokenParsing (Unhighlighted m)
-- Defined in ‘Text.Parser.Token’
...plus 11 others
In the expression: emptyIdents
In the expression:
emptyIdents {_styleStart = letter, _styleLetter = letter}
In an equation for ‘identStyle’:
identStyle
= emptyIdents {_styleStart = letter, _styleLetter = letter}
Failed, modules loaded: none.
Hah, this is kind of a funny problem.
The issue here is that emptyIdents
is class-polymorphic. So when you use it, some part of the type inference algorithm has to define which instance to use.
When you modify only one field at a time, the type of the record isn't permitted to change; that is, the type of \record -> record { _styleStart = undefined }
is IdentifierStyle m -> IdentifierStyle m
. So by demanding that the final type of
emptyIdents { _styleStart = letter } { _styleLetter = letter }
is IdentifierStyle m
, we can infer that the first emptyIdents
is also IdentifierStyle m
with the same type m
as argument.
On the other hand, thanks to how record update works in Haskell, when you update both fields at once (which happens to be all the fields whose types mention the type argument m
), the update becomes polymorphic. That is, the type of \record -> record { _styleStart = undefined, _styleLetter = undefined }
is IdentifierStyle m' -> IdentifierStyle m
-- note the prime!
So in the case where you do both updates at once like
emptyIdents { _styleStart = letter, _styleLetter = letter }
then fixing the final type of this update at IdentifierStyle m
does not determine the type of emptyIdents
.
There's half a dozen ways to fix this, but the basic idea is that you should fix an instance to use while building emptyIdents
.