Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 不明确的类型变量';废话';在约束中。。。如何修复?_Haskell_Type Variables - Fatal编程技术网

Haskell 不明确的类型变量';废话';在约束中。。。如何修复?

Haskell 不明确的类型变量';废话';在约束中。。。如何修复?,haskell,type-variables,Haskell,Type Variables,我正试图用Haskell编写一个简单的光线跟踪器。我想定义一个表示各种可用曲面的typeclass,并使用一个函数来确定光线与曲面相交的位置: {-# LANGUAGE RankNTypes #-} data Vector = Vector Double Double Double data Ray = Ray Vector Vector class Surface s where intersections :: s -> Ray -> [Vector] -- Obvio

我正试图用Haskell编写一个简单的光线跟踪器。我想定义一个表示各种可用曲面的typeclass,并使用一个函数来确定光线与曲面相交的位置:

{-# 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背景,我到底做错了什么?

在您调用的
getRenderableIntersections
函数中。解释器无法确定要使用的类
Surface
的实例。如果您有两个这样的实例:

instance Surface SurfaceA where
  -- ...
instance Surface SurfaceB where
  -- ...
解释器如何确定
表面的类型

定义
Renderable
的方式意味着存在一个函数
surface::surface s=>Renderable->s

尝试创建一个实例
SurfaceA
,并询问以下类型查询(给定一个简单的构造函数
SurfaceA
):

那么,这个表达式是什么类型的呢?我打赌你一定期待着
SurfaceA
。错。采用
曲面的类型
。它接受一个
Renderable
参数,我们将传递一个
Renderable
参数。那之后还剩下什么<代码>曲面s=>s
。这就是那个表达式的类型。我们仍然不知道
s
代表什么类型

如果希望类型为
SurfaceA
,则需要更改代码,使其类似于
surface::surface s=>Renderable s->s
。这样就可以确定
s
是什么,因为它与
Renderable
中使用的
s
相同

编辑:根据@mokus的建议,您也可以尝试扩展。它允许在类型声明的右侧“隐藏”类型参数

data Renderable = forall s. Surface s => Renderable
  { surface    :: s
  , otherStuff :: Int
  }
我上面链接的HaskellWiki页面甚至与您想要做的事情非常相似

编辑:(By@stumith)为了记录在案,我在下面包含了根据这些建议编译的代码。然而,我接受了这个答案,我认为这是一个更好的解决问题的方法

{-# 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

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

从函数的角度来看,您可以完全放弃这个类型类的表面概念。曲面是将光线映射到向量列表的东西,不是吗?因此:

type Surface = Ray -> [Vector]

data Renderable = Renderable
 { surface    :: Surface
 , otherStuff :: Int
}
现在,如果您真的需要,您可以拥有一个
ToSurface
typeclass,基本上与您给出的一样:

class ToSurface a where
     toSurface :: a -> Surface
但这只是为了方便和特殊的多态性。模型中的任何内容都不需要它

一般来说,存在主义的用例很少,但至少90%的情况下,你可以用它所代表的函数替换存在主义,得到更清晰、更容易推理的东西


此外,尽管这对您来说可能有点太多,而且问题并不完全匹配,但您可能会发现Conal关于指称设计的一些文章很有用:

…但既然所有曲面都实现了“交点”方法,为什么这还不够?很明显,在我的背景下,我将“Surface”视为一个抽象类,“Crossons”视为一个虚拟方法。@Stumith:请看我的更新。表达式中不能打开类型参数。要么整个表达式是参数化的,要么所有类型都可以静态确定。(可能有一个GHC扩展或其他允许的东西,但我不知道)那么这是否意味着Haskell没有与“虚拟”函数等价的函数?我希望有一个可渲染列表,并且能够将它们作为抽象类型处理。因此,我是否需要使用
[Renderable s]
?该列表是否可以包含具有不同
曲面
实例的异构
可渲染
实例?@stumith:
曲面
字段的类型不是您所期望的。这是所有s的
。曲面s=>s
,因此该字段中的值必须是每个
曲面
类型的有效值。唯一可能的此类值是
undefined
等(因为
Surface
类没有定义产生
s
的函数)。您要查找的是
存在类型
扩展,而不是
RankNTypes
,并且
曲面s=>
上下文将位于数据构造函数之前,即
数据可渲染=forall s。曲面s=>可渲染…
如果我正确记住了语法。这对谷歌来说已经足够了;)@莫库斯:谢谢你告诉我分机的名字。
class ToSurface a where
     toSurface :: a -> Surface