约束中的模糊类型变量'blah'...如何修复?

时间:2011-02-15 14:24:56

标签: haskell type-variables

我正在尝试在Haskell中编写一个简单的光线跟踪器。我想定义一个类型类,表示可用的各种表面,并有一个函数来确定光线与它们相交的位置:

{-# LANGUAGE RankNTypes #-}

data Vector = Vector Double Double Double
data Ray = Ray Vector Vector

class Surface s where
  intersections :: s -> Ray -> [Vector]

-- Obviously there would be some concrete surface implementations here...

data Renderable = Renderable
  { surface    :: (Surface s) => s
  , otherStuff :: Int
  }

getRenderableIntersections :: Renderable -> Ray -> [Vector]
getRenderableIntersections re ra = intersections (surface re) ra

然而,这给了我错误:

Ambiguous type variable 's' in the constraint:
  'Surface'
    arising from a use of 'surface'

(实际代码更复杂,但我试图将其提炼为更简单的东西,同时保持我想要实现的目标)。

我该如何解决这个问题?或者,鉴于我来自标准的OO背景,我从根本上做错了什么?

2 个答案:

答案 0 :(得分:10)

请不要使用存在类型!你可以,但没有意义。

从功能的角度来看,你可以完全放弃Surface的类型类概念。 Surface是将Ray映射到向量列表的东西,不是吗?所以:

type Surface = Ray -> [Vector]

data Renderable = Renderable
 { surface    :: Surface
 , otherStuff :: Int
}

现在,如果你真的想要,你可以拥有一个ToSurface类型,基本上就像你给出的那样:

class ToSurface a where
     toSurface :: a -> Surface

但这仅仅是为了方便和特殊的多态性。您的模型中没有任何内容需要它。

一般来说,存在的用例非常少,但至少有90%的时间可以用存在的方式代替它所代表的功能,并获得更清晰,更容易推理的东西。

此外,尽管你可能会过多地接受这些问题并且这些问题并不完全匹配,但你可能会发现Conal在指称性设计方面的一些内容很有用:http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics/

答案 1 :(得分:3)

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页面的an example非常类似于你想要做的事情。

编辑:(通过@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