修改可扩展记录时的实例重叠

时间:2019-03-30 19:40:01

标签: haskell

我正在项目中尝试可扩展记录(我正在使用行类型库),但是当我想以特定方式修改记录中的某些内容时遇到了问题,该返回非常有关重叠实例和不可推论类型的可怕错误。

我想表达的是一个函数,该函数获取包含特定标签类型对的记录,并可以修改该类型。但是当我尝试使用该功能时,会弹出可怕的错误。

我已将错误减少至以下示例。特别是启用IncoherentInstancesCould not deduce: (Rec.Modify "x" [Double] r .! "x") ~ [Double])之后的错误对我来说似乎很奇怪,因为它在那里立即说记录包含"x" :-> [Double]。 我尝试避免将Rec.Modifyf2 :: forall r. (HasType aes a r) => Rec r -> Rec (r .- aes .+ aes .== b)一起使用,但这会导致类似的错误。

我很乐意得到一些帮助,以弄清我做错了什么,以及如何进行类似的工作。

{-# LANGUAGE DataKinds, OverloadedLabels, TypeOperators, RankNTypes,
             RecordWildCards, NoMonomorphismRestriction #-}
module GoG.Temp where

import Data.Row
import qualified Data.Row.Records as Rec

type Scale' aes a = Scale aes a a
data Scale aes a b = Scale
  -- f1 works great
  -- f2 results in very scary errormessages
  { f1 :: forall r. (HasType aes a r) => Rec r -> Rec r
  -- What I'd like to express is a function that gets a record containing a
  -- specific label-type pair, and may modify that type. But when I try to
  -- use that function, the scary errors pop up
  , f2 :: forall r. (HasType aes a r) => Rec r -> Rec (Rec.Modify aes b r)
  -- ... other fields
  }

data Scales = Scales { _xScale :: Scale' "x" [Double] }

extractFromRecord :: (HasType "x" [Double] r, HasType "y" [Double] r) 
    => Rec r -> ()
extractFromRecord = undefined

render :: (HasType "x" [Double] r, HasType "y" [Double] r) 
    => Scales -> Rec r -> ()
-- If you replace f2 with f1 it works fine, but f2 results in the error
render Scales{..} r = extractFromRecord $ f2 _xScale r

这会导致以下错误:

    • Overlapping instances for HasType
                                  "x" [Double] (Rec.Modify "x" [Double] r)
        arising from a use of ‘extractFromRecord’
      Matching instances:
        instance forall k (r :: Row
                                  k) (l :: ghc-prim-0.5.3:GHC.Types.Symbol) (a :: k).
                 ((r .! l) ≈ a) =>
                 HasType l a r
          -- Defined in ‘Data.Row.Internal’
      There exists a (perhaps superclass) match:
        from the context: (HasType "x" [Double] r, HasType "y" [Double] r)
          bound by the type signature for:
                     render :: forall (r :: Row *).
                               (HasType "x" [Double] r, HasType "y" [Double] r) =>
                               Scales -> Rec r -> ()
          at /mnt/d/University/infoafp/afp-gog/src/GoG/Temp.hs:22:1-83
      (The choice depends on the instantiation of ‘r’
       To pick the first instance above, use IncoherentInstances
       when compiling the other instance declarations)
    • In the expression: extractFromRecord $ f2 _xScale r
      In an equation for ‘render’:
          render Scales {..} r = extractFromRecord $ f2 _xScale r
   |
23 | render Scales{..} r = extractFromRecord $ f2 _xScale r
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

或者如果我们按照建议启用IncoherentInstances,则执行以下操作:

    • Could not deduce: (Rec.Modify "x" [Double] r .! "x") ~ [Double]
        arising from a use of ‘extractFromRecord’
      from the context: (HasType "x" [Double] r, HasType "y" [Double] r)
        bound by the type signature for:
                   render :: forall (r :: Row *).
                             (HasType "x" [Double] r, HasType "y" [Double] r) =>
                             Scales -> Rec r -> ()
        at /mnt/d/University/infoafp/afp-gog/src/GoG/Temp.hs:23:1-83
    • In the expression: extractFromRecord $ f2 _xScale r
      In an equation for ‘render’:
          render Scales {..} r = extractFromRecord $ f2 _xScale r
    • Relevant bindings include
        r :: Rec r
          (bound at /mnt/d/University/infoafp/afp-gog/src/GoG/Temp.hs:24:19)
        render :: Scales -> Rec r -> ()
          (bound at /mnt/d/University/infoafp/afp-gog/src/GoG/Temp.hs:24:1)
   |
24 | render Scales{..} r = extractFromRecord $ f2 _xScale r
   |         

1 个答案:

答案 0 :(得分:2)

可怕的消息基本上无关紧要。这个简化的示例将HasType替换为(.!),并说明了这个问题,我认为您已经在上面发现了该问题:

{-# LANGUAGE DataKinds, FlexibleContexts, TypeOperators, GADTs #-}

module MyRow where

import Data.Row
import qualified Data.Row.Records as Rec

f2 :: ((r .! "x") ~ Double) => Rec r -> Rec (Rec.Modify "x" Double r)
f2 = undefined

extract :: ((r .! "x") ~ Double) => Rec r -> ()
extract = undefined

render :: ((r .! "x") ~ Double) => Rec r -> ()
render r = extract $ f2 r

此代码导致无法从(Modify "x" Double r .! "x") ~ Double推论(r .! "x") ~ Double的错误。这可能是“显然”正确的事实,但这并不意味着GHC可以证明这一点。

很高兴被证明是错误的,但是我认为您将被迫添加所需的显式约束。在您的原始示例中,以下类型签名(GHC可以自行推断出该类型签名)似乎起作用:

render ::
  ( HasType "x" [Double] r
  , HasType "x" [Double] (Rec.Modify "x" [Double] r)
  , HasType "y" [Double] (Rec.Modify "x" [Double] r)
  ) => Scales -> Rec r -> ()
render Scales{..} r = extractFromRecord $ f2 _xScale r

除了您在示例中启用的其他扩展程序之外,我还必须打开FlexibleContextsGADTs