这个OpenGL Haskell代码是如何工作的?
我通过直接跳入OpenGL来学习Haskell,但我似乎无法破解以下代码:这个OpenGL Haskell代码是如何工作的?,opengl,haskell,monads,Opengl,Haskell,Monads,我通过直接跳入OpenGL来学习Haskell,但我似乎无法破解以下代码: display :: DisplayCallback display = do let color3f r g b = color $ Color3 r g (b :: GLfloat) vertex3f x y z = vertex $ Vertex3 x y (z :: GLfloat) clear [ColorBuffer] renderPrimitive Quads $ do c
display :: DisplayCallback
display = do
let color3f r g b = color $ Color3 r g (b :: GLfloat)
vertex3f x y z = vertex $ Vertex3 x y (z :: GLfloat)
clear [ColorBuffer]
renderPrimitive Quads $ do
color3f 1 0 0
vertex3f 0 0 0
vertex3f 0 0.2 0
vertex3f 0.2 0.2 0
vertex3f 0.2 0 0
color3f 0 1 0
vertex3f 0 0 0
vertex3f 0 (-0.2) 0
vertex3f 0.2 (-0.2) 0
vertex3f 0.2 0 0
color3f 0 0 1
vertex3f 0 0 0
vertex3f 0 (-0.2) 0
vertex3f (-0.2) (-0.2) 0
vertex3f (-0.2) 0 0
color3f 1 0 1
vertex3f 0 0 0
vertex3f 0 0.2 0
vertex3f (-0.2) 0.2 0
vertex3f (-0.2) 0 0
flush
到目前为止,我的理解是:
显示
是一项功能问题:什么是DisplayCallbackdo
表示计算链,与IO单子有关color3f
和vertex3f
是本地函数,使用let
关键字在do
中定义了三个参数color
和vertex
是glColor*
和glVertex*
的openGL包装函数李>
现在这是让人困惑的地方:
和Color3
似乎是由三个参数组成的某种数据结构问题:为什么需要在此处使用数据结构?为什么API的设计者选择在这里使用它Vertex3
函数调用color3f
函数,并使用数据rgb传入单参数-数据结构color
。出于某种原因,此处仅为最后一个参数指定了数据类型Color3
问题:为什么只为最后一个参数指定了数据类型,为什么在此处指定(b::GLfloat)
- 问题:调用
函数color
时为什么使用美元符号color$Color3 r g(b::GLfloat)
clear
是openglglClear
的包装器,并将列表作为参数,在本例中,列表仅包含单个元素[ColorBuffer]
renderPrimitive
似乎是opengl的包装函数glBegin
,Quads
似乎是数据类型问题:接下来会发生什么?$do
表示什么?(一系列的计算?什么,什么?)flush
是glFlush
的包装器我不知道有问题的实际图书馆,但我想我可以回答你的一些观点 我想,
DisplayCallback
只是一些更复杂类型签名的别名。如果打开GHCi并导入必要的模块,您应该能够说
:i DisplayCallback
它会告诉你它指的是什么。然后至少你会看到定义,即使它不一定告诉你它的用途
do
符号不仅适用于IO单子,也适用于任何单子。如果OpenGL绘图操作发生在他们自己的专业monad中,我不会感到惊讶
为什么指定了GLFloat
?我想象Color3
和Vector3
被定义为保存任何类型的坐标。我们希望它是GLFloat
,因此是类型签名。为什么它只在一个坐标上?我可以想象,Color3
的定义要求所有坐标都具有相同的类型,因此为一个坐标指定它会自动导致其他两个坐标具有相同的类型
为什么Color3
甚至存在?为什么我们不能用三个输入调用color
?好吧,这是一个API设计选择,但如果Color3
和Vector3
的定义允许您对整个向量或颜色进行算术运算,我也不会感到惊讶。因此,通过将坐标放入一个向量,你可以将它们视为一个单独的单元,对它们进行算术运算,轻松地将它们存储在列表中,等等。如果你正在做这些,你就不需要为了调用OpenGL函数而再次将它们全部解包
美元符号是什么意思?好吧,如果我写信
color Color3 r g b
这意味着我正在调用color
,向它传递四个参数:Color3
、r
、g
和b
。这不是我们想要的。我们想要的是
color (Color3 r g b)
也就是说,使用一个参数调用color
。您可以使用括号,也可以使用$
。它有点像Unix管道,因为它允许您
func3 (func2 (func1 x))
进入
如果您有很多函数,那么获取正确数量的右括号可能会很烦人。对于一个函数调用,这是一个品味的问题
renderPrimitive
函数有两个参数。其中一个是Quads
(不管是什么),另一个是整个do块。因此,您将所有代码作为参数传递给renderPrimitive
函数(该函数可能以某种方式执行它)
希望这能给你一些启示。1
DisplayCallback
是IO()
的类型同义词。您可以使用hoogle查找内容,或者在ghci中键入:i DisplayCallback
之所以给IO()
一个特殊名称,是因为GLUT
基于回调:注册函数以处理特定事件,例如显示、输入事件、调整事件大小等。有关完整列表,请参阅。显然,您不需要类型同义词,但它们是为了清晰和更好的交流而提供的
二,。“我假设color and vertex是glColor*和glVertex*的openGL包装函数”不完全-openGL
是更基本的OpenGLRaw
的包装,它是c openGL库到haskell的1:1映射vertex
和color
比glColor
和glVertex
稍微复杂一些,但您可能会假设在大多数情况下它们是相同的
更具体地说,vertex
是vertex
类的成员,该类有4个实例Vertex4
、Vertex3
和Vertex2
三,Color3
定义为data Color3 a=Color3!A.A.a
。为什么这是必要的?嗯,他们可以很容易地使用(a,a,a)->IO()
func3 $ func2 $ func1 $ x
glBegin (GL_QUADS);
// All the stuff inside goes here ...
glEnd ();