重构构造函数的类的实例

时间:2017-05-15 11:12:13

标签: haskell typeclass type-constructor

我想创建一个类型类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 :. bData.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 b的实例而没有基本案例(:.) a ()的显式实例?

1 个答案:

答案 0 :(得分:1)

您必须使归纳实例与基本案例实际区分开来。现在,((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

另外,原则上更优雅的是Alexey Romanov所建议的:使基本情况为零维度。但显然没有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