1 . DisplayCallback
is a type synonym for IO ()
. You can use hoogle to look things up, or type :i DisplayCallback
in ghci.
The reason that IO ()
is given a special name is because GLUT
is based on callbacks: functions are registered to handle a specific event, such as displaying, input events, resize events, etc. For the full list, see the docs. You don't need the type synonyms, obviously, but they are provided for clarity and better communication.
2 . "I assume that color and vertex is an openGL wrapper function for glColor* and glVertex*." Not quite - OpenGL
is a wrapper around the more basic OpenGLRaw
, which is a 1:1 mapping of the c opengl library to haskell. vertex
and color
are slightly more sophisticated that glColor
and glVertex
, but you can probably assume for most uses they are the same.
More specifically, vertex
is a member of the Vertex
class which has 4 instances Vertex4
, Vertex3
, and Vertex2
.
3 . Color3
is defined as data Color3 a = Color3 !a !a !a
. The exclamation indicates that the fields are strict.. Why is this necessary? Well, they could have easily used (a,a,a) -> IO ()
or a -> a -> a -> IO ()
but then a function taking a color is indistinguishable from one taking a vector, which are "ideologically" different objects, even if they are represented by precisely the same data (Vertex is data Vertex3 a = Vertex3 !a !a !a
). So you don't want to be able to pass a vertex to a function requiring a color. Also, these datatypes give better performance, ideally, due to the strictness.
4 . Why is the type specified at all? Short answer, is the type system requires it. The type of a literal is Num a => a
which is too polymorphic for the datatype, which requires a concrete type. So you pick said concrete type by using a type annotation.
Why is it only required in one place? The compiler can infer the type of the other fields. Look back to the data type decleration - all three fields must have the same type. If one field is annotated, the rest are trivially inferred. You can also write Color3 r (g :: GLfloat) b
or Color3 (r :: GLfloat) g b
or give the function vertex3f
a type signature.
5 . The $
is just function application with low precedence, it is defined as f $ a = f a; infixr $ 0
. You can also write color (Color3 r g (b :: GLfloat))
so it is purely a matter of style here.
6 . Perhaps the docs will again best explain what renderPrimitive
is doing. But the short version is this: instead of writing things inside of a glBegin
- glEnd
block, you write it inside of renderPrimitive
. You don't need to write glEnd
because this is hidden inside of renderPrimitive
. So your code is equivalent to:
glBegin (GL_QUADS);
// All the stuff inside goes here ...
glEnd ();
The OpenGL
does some magic in order to bring exceptions from c into the haskell universe properly, but you don't really have to worry about that.
Final comment: If you plan to use haskell for graphics, the writing actual openGL code is probably not the best idea. After all, if you are going to use openGL, why not just use c? There are a whole myriad of graphics libraries out there. You should browse hackage for a package that is right for you. I'm afraid I can't recommend anything as I'm not familiar with what packages are available.