3

次のコードは、「あいまいな型変数」エラーで失敗します (下)。ただし、レコードを 2 回更新する別の定義は問題なく機能します。これはなぜでしょうか? さらに、以下の「壊れた」定義は、 Trifecta ソースのものと非常によく似ています。私は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.
4

1 に答える 1

4

うーん、これはちょっと面白い問題です。

ここでの問題は、それemptyIdentsがクラス ポリモーフィックであることです。したがって、それを使用する場合、型推論アルゴリズムの一部で、使用するインスタンスを定義する必要があります。

一度に 1 つのフィールドのみを変更する場合、レコードのタイプは変更できません。つまり、の型は\record -> record { _styleStart = undefined }isIdentifierStyle m -> IdentifierStyle mです。したがって、最終型の

emptyIdents { _styleStart = letter } { _styleLetter = letter }

である場合、最初のものも引数と同じ型であるとIdentifierStyle m推測できます。emptyIdentsIdentifierStyle m m

一方、Haskell でのレコード更新のしくみのおかげで、両方のフィールドを一度に更新すると (これはたまたま型が type 引数 を言及しているすべてのフィールドですm)、更新は多態的になります。つまり、 の型\record -> record { _styleStart = undefined, _styleLetter = undefined }IdentifierStyle m' -> IdentifierStyle m-- 素数に注意してください!

したがって、両方の更新を一度に行う場合

emptyIdents { _styleStart = letter, _styleLetter = letter }

次に、この更新の最終型を に修正しても、 の型は決定されIdentifierStyle mませemptyIdents

これを修正する方法は 6 通りありますが、基本的な考え方は、ビルド中に使用するインスタンスを修正する必要があるということですemptyIdents

于 2016-01-27T19:05:52.497 に答える