В вашей функции getRenderableIntersections
вы вызываете surface
. Интерпретатор не может выяснить, какой экземпляр класса Surface
вы хотите использовать. Если у вас есть два таких экземпляра:
instance Surface SurfaceA where
-- ...
instance Surface SurfaceB where
-- ...
Как переводчик может определить тип surface
?
То, как вы определили Renderable
, означает, что есть функция surface :: Surface s => Renderable -> s
.
Попробуйте создать экземпляр Surface SurfaceA
и задать следующий запрос типа (с учетом простого конструктора SurfaceA
):
> :t surface (Renderable SurfaceA 0) -- What's the type of the expression?
Итак, какого типа это выражение? Могу поспорить, что вы ожидаете SurfaceA
. Неправильно. Возьмите тип surface
. Он принимает аргумент Renderable
, и мы передаем ему аргумент Renderable
. Что осталось после этого? Surface s => s
. Это тип этого выражения. Мы до сих пор не знаем, какой тип представляет s
.
Если вы хотите, чтобы тип был SurfaceA
, вам нужно изменить свой код, чтобы он стал чем-то вроде surface :: Surface s => Renderable s -> s
. Таким образом, можно определить, что такое s
, потому что это то же самое s
, которое используется в Renderable
.
РЕДАКТИРОВАТЬ: Как предложено @mokus, вы также можете попробовать расширение ExistentialTypes
. Это позволяет "скрывать" параметры типа справа от объявления типа.
data Renderable = forall s. Surface s => Renderable
{ surface :: s
, otherStuff :: Int
}
На странице HaskellWiki, на которую я ссылался выше, даже есть пример , очень похожий на то, что вы хотите сделать.
РЕДАКТИРОВАТЬ: (@stusmith) - Для справки, я включаю код ниже, который компилируется на основе этих предложений здесь. Однако я принял ответ, который, я думаю, показывает лучший способ приблизиться к вещам.
{-# LANGUAGE ExistentialQuantification #-}
data Vector = Vector Double Double Double
data Ray = Ray Vector Vector
class Surface_ s where
intersections :: s -> Ray -> [Vector]
data Surface = forall s. Surface_ s => Surface s
instance Surface_ Surface where
intersections (Surface s) ra = intersections s ra
data Renderable = Renderable
{ surface :: Surface
}
getRenderableIntersections :: Renderable -> Ray -> [Vector]
getRenderableIntersections re ra = intersections (surface re) ra