一次更新两个记录字段时类型变量不明确(但更新一个字段时不是!)
Ambiguous type variable when updating two record fields at a time (but not when updating one!)
以下代码失败并出现 "ambiguous type variable" 错误(底部)。然而,更新记录两次的替代定义工作得很好:这是为什么?此外,下面的 "broken" 定义与 Trifecta source 中的定义非常相似。我正在针对 trifecta 1.5.2 和解析器 0.12.3 使用 GHC 7.10.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
}
这是工作(替代)定义
identStyle :: TokenParsing m => IdentifierStyle m
identStyle = T.emptyIdents { _styleStart = letter } { _styleLetter = letter }
第一个定义产生的错误是:
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.
哈,这是个有趣的问题。
这里的问题是 emptyIdents
是 class-polymorphic。所以当你使用它时,类型推断算法的某些部分必须定义要使用的实例。
当您一次只修改一个字段时,不允许更改记录的类型;也就是说,\record -> record { _styleStart = undefined }
的类型是 IdentifierStyle m -> IdentifierStyle m
。所以通过要求最终类型
emptyIdents { _styleStart = letter } { _styleLetter = letter }
是IdentifierStyle m
,我们可以推断第一个emptyIdents
也是IdentifierStyle m
与参数m
相同的类型.
另一方面,由于记录更新在 Haskell 中的工作方式,当您同时更新两个字段时(恰好是类型提及类型参数 m
的所有字段) ,更新变成多态的。也就是说,\record -> record { _styleStart = undefined, _styleLetter = undefined }
的类型是 IdentifierStyle m' -> IdentifierStyle m
-- 注意素数!
因此,如果您同时进行两个更新,例如
emptyIdents { _styleStart = letter, _styleLetter = letter }
然后在 IdentifierStyle m
修复此更新的最终类型 而不是 确定 emptyIdents
的类型。
有六种方法可以解决此问题,但基本思想是您应该修复一个实例以在构建时使用 emptyIdents
。
以下代码失败并出现 "ambiguous type variable" 错误(底部)。然而,更新记录两次的替代定义工作得很好:这是为什么?此外,下面的 "broken" 定义与 Trifecta source 中的定义非常相似。我正在针对 trifecta 1.5.2 和解析器 0.12.3 使用 GHC 7.10.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
}
这是工作(替代)定义
identStyle :: TokenParsing m => IdentifierStyle m
identStyle = T.emptyIdents { _styleStart = letter } { _styleLetter = letter }
第一个定义产生的错误是:
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.
哈,这是个有趣的问题。
这里的问题是 emptyIdents
是 class-polymorphic。所以当你使用它时,类型推断算法的某些部分必须定义要使用的实例。
当您一次只修改一个字段时,不允许更改记录的类型;也就是说,\record -> record { _styleStart = undefined }
的类型是 IdentifierStyle m -> IdentifierStyle m
。所以通过要求最终类型
emptyIdents { _styleStart = letter } { _styleLetter = letter }
是IdentifierStyle m
,我们可以推断第一个emptyIdents
也是IdentifierStyle m
与参数m
相同的类型.
另一方面,由于记录更新在 Haskell 中的工作方式,当您同时更新两个字段时(恰好是类型提及类型参数 m
的所有字段) ,更新变成多态的。也就是说,\record -> record { _styleStart = undefined, _styleLetter = undefined }
的类型是 IdentifierStyle m' -> IdentifierStyle m
-- 注意素数!
因此,如果您同时进行两个更新,例如
emptyIdents { _styleStart = letter, _styleLetter = letter }
然后在 IdentifierStyle m
修复此更新的最终类型 而不是 确定 emptyIdents
的类型。
有六种方法可以解决此问题,但基本思想是您应该修复一个实例以在构建时使用 emptyIdents
。