构造函数的类型类的重叠实例



我想创建一个类型类GetGLInfo,其中包含多个OpenGL函数所需的一些基本类型信息:

class GetGLInfo v where
    numComponents :: v -> GL.GLint
    bytesPerComponent :: v -> Int
    openGLType :: v -> GL.GLenum

一个简单的实例是Float

instance GetGLInfo Float where
    numComponents _ = 1
    bytesPerComponent = sizeOf
    openGLType _ = GL.GL_FLOAT

我想使用构造函数data a :. b为 Data.Vec 矢量数据类型创建此类的实例:

import qualified Data.Vec as Vec
instance GetGLInfo a => GetGLInfo ((Vec.:.) a ()) where
    numComponents _ = 1
    bytesPerComponent = (bytesPerComponent).(Vec.head)
    openGLType = (openGLType).(Vec.head)
instance (GetGLInfo a, GetGLInfo b) => GetGLInfo ((Vec.:.) a b) where
    numComponents (_ Vec.:. r)= 1 + (numComponents r)
    bytesPerComponent = (bytesPerComponent).(Vec.head)
    openGLType = (openGLType).(Vec.head)

我会使用numComponents = Vec.length但我遇到了更多的类型错误......

调用openGLType Vec2时,出现以下错误:

let c = openGLType ((0.0 :. 1.0 :. ()) :: Vec2 Float)
error:
* Overlapping instances for GetGLInfo (Float :. ())
    arising from a use of `openGLType'
  Matching instances:
    instance GetGLInfo a => GetGLInfo (a :. ())
      -- Defined in `GLTypeInfo'
    instance (GetGLInfo a, GetGLInfo b) => GetGLInfo (a :. b)
      -- Defined in `GLTypeInfo'
* In the expression: openGLType ((0.0 :. 1.0 :. ()) :: Vec2 Float)
  In an equation for `c':
      c = openGLType ((0.0 :. 1.0 :. ()) :: Vec2 Float)

为什么我的实例在这里重叠?有没有一种简单的方法可以在没有基本情况(:.) a ()显式实例的情况下创建(:.) a b实例?

您必须使归纳实例实际上与基本情况区分开来。现在,((Vec.:.) a ())只是((Vec.:.) a b)的一个特例,这就是"重叠实例"的意思。但是您希望后者仅在b本身已经是非零维时才匹配。好吧,明确说明:

instance (GetGLInfo a, GetGLInfo b, GetGLInfo xs) => GetGLInfo (a:.b:.xs) where
  numComponents (_:.b:.xs)= 1 + numComponents (b:.xs)
  bytesPerComponent = bytesPerComponent . Vec.head
  openGLType = openGLType . Vec.head

或者,原则上更优雅的是阿列克谢·罗曼诺夫的建议:使基本情况为零维。但显然没有GL_VOID,所以这里没有选择。

作为一种完全不同的方法,我建议只为标量创建自己的类,然后在任意向量空间上进行泛化:

import Data.VectorSpace
import Data.Basis
class GLScalar s where
  scalar_bytesPerComponent :: Functor proxy => proxy s -> Int
  scalar_openGLType :: Functor proxy => proxy s -> GL.GLenum
instance GLScalar Float
bytesPerComponent :: ∀ v proxy
        . (VectorSpace v, GLScalar (Scalar v), Functor proxy)
             => proxy v -> Int
bytesPerComponent _ = scalar_bytesPerComponent ([]::[Scalar v])
numComponents :: ∀ v . HasBasis v => v -> Int
numComponents = length . decompose

最新更新