Проблемы с ISubset в виниле
У меня есть следующий код:
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
, Что здесь происходит?
2 ответа
Глядя на исходный код для винила, IElem x xs
(который является синонимом Implicit (Elem x xs)
) имеет два экземпляра:
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)
Хаскелл не может вывести соответствующий экземпляр. Более того, ни один такой экземпляр не может быть записан, потому что это может привести к наложению некоторого неприятного экземпляра.
В качестве возможного обходного пути, мы можем манипулировать свидетелями членства (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
...
Из краткого обзора винилового кода кажется, что идея "подмножества" не была полностью реализована (имеется в виду, что для того, чтобы она была действительно полезной, требуется больше функций и экземпляров). Не могли бы вы вместо этого использовать отношения "подтип" между записями? Тогда вы можете сделать
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