2

次のコードがあります。

type Drawable = '["object" ::: Object, "transform" ::: M44 GL.GLfloat]
objXfrm :: "transform" ::: M44 GL.GLfloat
objXfrm = Field
objRec :: "object" ::: Object
objRec = Field

drawObject :: (Drawable `ISubset` a) => M44 GL.GLfloat -> PlainRec a -> IO ()
drawObject camera obj =
    withVAO vao $ do
        GL.currentProgram $= Just (program shdr)
        setUniforms shdr (modelView =: (rGet objXfrm obj !*! camera))
        GL.polygonMode $= (GL.Line, GL.Line)
        GL.drawElements GL.Triangles inds GL.UnsignedInt nullPtr
    where Object {objVAO = vao, objNumIndices = inds, objShader = shdr}
              = rGet objRec obj

タイプを取り除くと、drawObject問題なくコンパイルされますが、タイプを取得すると

  Could not deduce (IElem * ("transform" ::: V4 (V4 GL.GLfloat)) a)
      arising from a use of `rGet'
    from the context (ISubset * Drawable a)
...
  Could not deduce (IElem * ("object" ::: Object) a)
      arising from a use of `rGet'
    from the context (ISubset * Drawable a)

GHCが私のために推測するタイプは

drawObject
  :: (IElem * ("object" ::: Object) rs,
      IElem * ("transform" ::: V4 (V4 GL.GLfloat)) rs) =>
     V4 (V4 GL.GLfloat)
     -> Rec rs Data.Functor.Identity.Identity -> IO ()

そして、それは型シグネチャとしてうまく機能しますが、 を持つものはそうでISubsetはありません。引数を に交換しても、エラーはまったく同じですISubset。ここで何が起こっているのですか?

4

2 に答える 2

3

Vinyl IElem x xs(のシノニムImplicit (Elem x xs)) のソース コードを見ると、2 つのインスタンスがあります。

instance Implicit (Elem x (x ': xs)) where
    implicitly = Here
instance Implicit (Elem x xs) => Implicit (Elem x (y ': xs)) where
    implicitly = There implicitly

ここでは言及されていないことに注意してSubsetください。論理的に(x ∈ xs) ∧ (xs ⊆ ys) ⇒ (x ∈ ys)は ですが、署名 を持つインスタンスがないためImplicit (Subset xs ys), Implicit (Elem x xs) => Implicit (Elem x ys)、Haskell には適切なインスタンスを推測する方法がありません。さらに、そのようなインスタンスを書き込むことはできません。これを行うと、厄介なインスタンスのオーバーラップが発生するためです。

可能な回避策として、適切なインスタンスを強制するために、メンバーシップの監視 (ElemおよびSubset) を直接操作できます (これは完全にテストされておらず、惨めに失敗する可能性があります)。

{-# LANGUAGE RankNTypes, ScopedTypeVariables, and possibly more... #-}

-- Lift an Elem value to a constraint.
withElem :: Elem x xs -> (forall r. IElem x xs => r) -> r
withElem Here x = x
withElem (There e) x = withElem e x

-- Witness of (x ∈ xs) ⇒ (xs ⊆ ys) ⇒ (x ∈ ys)
subsetElem :: Elem x xs -> Subset xs ys -> Elem x ys
subsetElem Here (SubsetCons e _) = e
subsetElem (There e) (SubsetCons _ s) = There (subsetElem e s)

-- Utility for retrieving Elem values from Fields (:::).
fieldElem :: IElem x xs => x -> Elem x xs
fieldElem _ = implicitly

inSubset :: IElem x xs => x -> Subset xs ys -> (forall r. IElem x ys => r) -> r
inSubset f s x = withElem (subsetElem (fieldElem f) s) x

drawObject :: forall a. (Drawable `ISubset` a) => M44 GL.GLfloat-> PlainRec a -> IO ()
drawObject camera obj =
    inSubset objRec subset $
    inSubset objXfrm subset $
        -- The desired instances should now be available here
        ...
  where
    subset = implicitly :: Subset Drawable a
    ...
于 2014-02-19T06:19:05.847 に答える
2

Vinyl コードをざっと見てみると、「サブセット」のアイデア全体が完全には実装されていないように見えます (つまり、実際に役立つには、より多くの関数とインスタンスが必要です)。代わりに、レコード間の「サブタイプ」関係を使用することはできませんか? それからあなたはすることができます

drawObject :: (PlainRec a <: PlainRec Drawable) => M44 GL.GLfloat -> PlainRec a -> IO ()
drawObject camera obj' =
    ... -- as before
  where
    obj :: PlainRec Drawable
    obj = cast obj'
    ... -- rest as before
于 2014-02-19T08:00:01.170 に答える