Ошибка компиляции шаблона Haskell

Рассмотрим следующий код:

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoMonomorphismRestriction #-}

import Data.HList.GhcSyntax((.!.),(.=.),(.*.))
import Data.HList.Record(emptyRecord)
import Data.HList.TypeCastGeneric1
import Data.HList.TypeEqGeneric1
import Data.HList.Label5

data Hello1 = Hello1
data Hello2 = Hello2

record = (Hello1 .=. "Hello1") .*. (Hello2 .=. "Hello2") .*. emptyRecord

f1 = $([| (\r1 -> (r1 .!. Hello1)) |]) 

main = print $ f1 record

Это компилируется нормально и выводит «Hello1», как и ожидалось.

Однако добавление следующей строки (GHC 7.4.1) приводит к ошибке компиляции:

f2 = $([| (\r2 -> (r2 .!. Hello2)) |]) 

Указана ошибка:

error.hs:16:1:
    Could not deduce (Data.HList.Record.HasField Hello2 r0 v0)
      arising from the ambiguity check for `main'
    from the context (Data.HList.Record.HasField Hello2 r v)
      bound by the inferred type for `main':
                 Data.HList.Record.HasField Hello2 r v => IO ()
      at error.hs:(16,1)-(20,38)
    Possible fix:
      add an instance declaration for
      (Data.HList.Record.HasField Hello2 r0 v0)
    When checking that `main'
      has the inferred type `forall r v.
                             Data.HList.Record.HasField Hello2 r v =>
                             IO ()'
    Probable cause: the inferred type is ambiguous

error.hs:16:1:
    Could not deduce (Data.HList.Record.HasField Hello2 r0 v0)
      arising from the ambiguity check for `f1'
    from the context (Data.HList.Record.HasField Hello2 r v)
      bound by the inferred type for `f1':
                 Data.HList.Record.HasField Hello2 r v =>
                 Data.HList.Record.Record
                   (Data.HList.HListPrelude.HCons
                      (Data.HList.Record.LVPair Hello1 [Char])
                      (Data.HList.HListPrelude.HCons
                         (Data.HList.Record.LVPair Hello2 [Char])
                         Data.HList.HListPrelude.HNil))
                 -> [Char]
      at error.hs:(16,1)-(20,38)
    Possible fix:
      add an instance declaration for
      (Data.HList.Record.HasField Hello2 r0 v0)
    When checking that `f1'
      has the inferred type `forall r v.
                             Data.HList.Record.HasField Hello2 r v =>
                             Data.HList.Record.Record
                               (Data.HList.HListPrelude.HCons
                                  (Data.HList.Record.LVPair Hello1 [Char])
                                  (Data.HList.HListPrelude.HCons
                                     (Data.HList.Record.LVPair Hello2 [Char])
                                     Data.HList.HListPrelude.HNil))
                             -> [Char]'
    Probable cause: the inferred type is ambiguous

Почему добавление строки f2 приводит к ошибке компиляции?

Примечание. Части Template Haskell здесь могут выглядеть глупо, но они представляют собой упрощение более сложного Template Haskell, который работает с кортежами. Я опубликовал самый простой пример, который я мог построить, который все еще демонстрировал ошибку. Я понимаю, что в данном случае удаление Template Haskell решает проблему, но в моем реальном коде это невозможно.

Изменить:

Кроме того, следующее не удается скомпилировать. Почему это так:

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoMonomorphismRestriction #-}

import Data.HList.GhcSyntax((.!.),(.=.),(.*.))
import Data.HList.Record(emptyRecord)
import Data.HList.TypeCastGeneric1
import Data.HList.TypeEqGeneric1
import Data.HList.Label5

data Hello1 = Hello1
data Hello2 = Hello2
data Hello3 = Hello3

record1 = (Hello1 .=. "Hello1") .*. (Hello2 .=. "Hello2") .*. emptyRecord
record2 = (Hello1 .=. "Hello1") .*. (Hello2 .=. "Hello2") .*. (Hello3 .=. "Hello3") .*. emptyRecord

f1 = $([| (\r1 -> (r1 .!. Hello1)) |]) 

main = print $ (f1 record1, f1 record2)

person Clinton    schedule 27.08.2012    source источник
comment
После добавления определения для f2 вы используете его где-нибудь? Если нет, сработает ли это, если вы дадите f2 явную аннотацию типа?   -  person Daniel Wagner    schedule 27.08.2012
comment
@DanielWagner: Использование f2, кажется, помогает. Однако я обнаружил другие проблемы. См. раздел stackoverflow.com/questions/12144250/ пример проблемы, не связанной с HList.   -  person Clinton    schedule 27.08.2012
comment
@DanielWagner: Если подумать, возможно, этот вопрос - отвлекающий маневр, хотя он может быть связан. Я нашел обходной путь и поставил его как ответ на свой вопрос, это не идеальное, но работоспособное решение для моей проблемы.   -  person Clinton    schedule 27.08.2012


Ответы (1)


Я обнаружил, что предоставление сигнатур типов функций верхнего уровня устраняет любые проблемы. См. код ниже:

{-# LANGUAGE TemplateHaskell #-}

module X where
  import Data.HList.GhcSyntax((.!.))

  f = [| (\x r -> (r .!. x)) |]
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleContexts #-}

import Data.HList.GhcSyntax((.!.),(.=.),(.*.))
import Data.HList.Record(emptyRecord)
import Data.HList.TypeCastGeneric1
import Data.HList.TypeEqGeneric1
import Data.HList.Label5
import X
import Data.HList.Record (HasField)

data Hello1 = Hello1
data Hello2 = Hello2
data Hello3 = Hello3

record1 = (Hello1 .=. "Hello1") .*. (Hello2 .=. "Hello2") .*. emptyRecord
record2 = (Hello1 .=. "Hello1") .*. (Hello2 .=. "Hello2") .*. (Hello3 .=. "Hello3") .*. emptyRecord

g1 :: (HasField Hello1 a b) => a -> b -- Type signature here
g1 = $(f) Hello1

g2 :: (HasField Hello2 a b) => a -> b -- Type signature here
g2 = $(f) Hello2

main = print $ (g1 record1, g2 record1, g1 record2, g2 record2)
person Clinton    schedule 27.08.2012