3

以下代码因“不明确的类型变量”错误而失败(底部)。但是,更新记录两次的替代定义可以正常工作:这是为什么呢?此外,下面的“损坏”定义与Trifecta 源中的定义非常相似。我正在使用 GHC 7.10.3 针对 trifecta 1.5.2 和解析器 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
    }

这是工作(替代)定义

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.
4

1 回答 1

4

哈,这是一个有趣的问题。

这里的问题emptyIdents是类多态。因此,当您使用它时,类型推断算法的某些部分必须定义要使用的实例。

一次只修改一个字段时,记录的类型是不允许改变的;也就是说,类型\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

于 2016-01-27T19:05:52.497 回答