I would like to create a type class GetGLInfo that holds some basic type information needed by multiple OpenGL functions:
class GetGLInfo v where
numComponents :: v -> GL.GLint
bytesPerComponent :: v -> Int
openGLType :: v -> GL.GLenum
An easy instance is Float:
instance GetGLInfo Float where
numComponents _ = 1
bytesPerComponent = sizeOf
openGLType _ = GL.GL_FLOAT
I would like to create an instance of this class for the Data.Vec vector data type with the constructor data a :. b:
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)
I wouldve used numComponents = Vec.length but I got more type errors...
When calling openGLType for a Vec2 I get the following error:
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)
Why do my instances overlap here?
Is there a easy way to create an instance of (:.) a b without an explicit instance for the base case (:.) a ()?
You must make the inductive instance actually distinguishable from the base case. Right now,
((Vec.:.) a ())is merely a special case of((Vec.:.) a b), which is what “overlapping instance” means. But you want the latter to only match ifbis itself already non-zero-dimensional. Well, make that explicit:Alternatively and in principle more elegant is what Alexey Romanov suggests: make the base case zero-dimensional. But apparently there is no
GL_VOID, so not an option here.As a completely different approach, I would suggest only making your own class for scalars, and then generalising over arbitrary vector spaces: