Haskellを初めから

  • こちらがよいことに気づいた
  • Stackを入れて、新しいプロジェクト "h170505"を作る
  • 冒頭のサイトにも紹介されているOpenGLTutorial1をやってみる
  • Main.hsの書き換え
    • .../h170505/app/Main.hs を書き換える
import Graphics.UI.GLUT
 
main :: IO ()
main = do
  (_progName, _args) <- getArgsAndInitialize
  _window <- createWindow "Hello World"
  displayCallback $= display
  mainLoop
 
display :: DisplayCallback
display = do
  clear [ ColorBuffer ]
  flush
    • をコピペして
    • .../h170505/ 以下で
stack install
    • すると、エラーになる、GLUTが...と。
    • 書き換えたMain.hsでimport Graphics.UI.GLUTしているのにそのことを.cabalファイルに教えていないから
    • Main.hsで読んでいるパッケージなので、.../h170505/h170505.cabalファイルのMain.hsに対応して実行可能ファイルをつくるための欄を以下のように書き換える
executable h170505-exe
  hs-source-dirs:      app
  main-is:             Main.hs
  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
  build-depends:       base
                     , h170505
                     , GLUT
                     , OpenGL
  default-language:    Haskell2010
    • その上で、再度
stack install
    • 実行ファイルh170505-exeができるので
h170505-exe
    • とすると、Hello Worldというタイトルのウィンドウが現れる
  • Main.hsをさらに書き換え
    • myPointsという GLfloatの3つ組のリストを用意し、それをリスト内包表現で12個の点として定義する
    • その点座標からOpenGLが点として認識するらしきVertexに変える
    • 変えるにあたり、すべての点にラムダ関数 (\始まり)をmapM_で適用する
import Graphics.UI.GLUT
 
myPoints :: [(GLfloat,GLfloat,GLfloat)]
myPoints = [ (sin (2*pi*k/12), cos (2*pi*k/12), 0) | k <- [1..12] ]
 
main :: IO ()
main = do
  (_progName, _args) <- getArgsAndInitialize
  _window <- createWindow "Hello World"
  displayCallback $= display
  mainLoop
 
display :: DisplayCallback
display = do 
  clear [ColorBuffer]
  renderPrimitive Points $
     mapM_ (\(x, y, z) -> vertex $ Vertex3 x y z) myPoints
  flush
    • mapM_というmap関数の特殊形のようなものがなんだかわからないので調べることにする
    • .../h170505/以下で
stack ghci
    • とすると、現在の環境でのインターラクティブ版 ghciが現れるので
:type mapM_
    • と、この関数IDのタイプを尋ねると
mapM_ :: (Monad m, Foldable t) => (a -> m b) -> t a -> m ()
    • とのこと
    • "(\(x, y, z) -> vertex $ Vertex3 x y z)"が(a -> m b)
    • "myPoints"がt a
    • myPointsはリストなのでFoldable
    • (x,y,z)はリストであるmyPointsの個々の要素で、3つ組
    • 関数 vertex も:type vertex で調べると
:type vertex
vertex :: Vertex a => a -> IO()
    • であり、VertexをIO()モナドに変える
      • "$ Vertex3 x y z"は"(Vertex3 x y z)$"のことで、Vertex3 に3つの値を与えて作ったVertexを vertexに与えてIO()モナドにする
    • 結局、モナド mのタプル () ができる
  • 点の数を増やしてみる
    • myKにInt型指定をして実行してみると、エラーが出る。myPointsの定義行ではFloatとして振舞っているから
    • 型を指定せずにhaskellに型を選ばせておけばよい
import Graphics.UI.GLUT
 
myK = 120

myPoints :: [(GLfloat,GLfloat,GLfloat)]
--myPoints = [ (sin (2*pi*k/12), cos (2*pi*k/12), 0) | k <- [1..12] ]
myPoints = [ (sin (2*pi*k/myK), cos (2*pi*k/myK), 0) | k <- [1..myK] ]
 
main :: IO ()
main = do
  (_progName, _args) <- getArgsAndInitialize
  _window <- createWindow "Hello World"
  displayCallback $= display
  mainLoop
 
display :: DisplayCallback
display = do 
  clear [ColorBuffer]
  renderPrimitive Points $
     mapM_ (\(x, y, z) -> vertex $ Vertex3 x y z) myPoints
  flush
  • renderPrimitive
    • render Pointsというのは、mapM_が作る描図用のオブジェクトを個別に打点する
    • render Trianglesとすると、3つずつを取り出して三角形を描く
import Graphics.UI.GLUT
 
myK = 12

myPoints :: [(GLfloat,GLfloat,GLfloat)]
--myPoints = [ (sin (2*pi*k/12), cos (2*pi*k/12), 0) | k <- [1..12] ]
myPoints = [ (sin (2*pi*k/myK), cos (2*pi*k/myK), 0) | k <- [1..myK] ]
 
main :: IO ()
main = do
  (_progName, _args) <- getArgsAndInitialize
  _window <- createWindow "Hello World"
  displayCallback $= display
  mainLoop
 
display :: DisplayCallback
display = do 
  clear [ColorBuffer]
  --renderPrimitive Points $
  renderPrimitive Triangles $
     mapM_ (\(x, y, z) -> vertex $ Vertex3 x y z) myPoints
  flush
  • 同様に…
    • Triangles をTriangleStripに換えたり、TriangleFanに換えたりすれば、それぞれの仕様で表示される
    • Lines, LikeLoop, LineStrip, Quads, QuadStrip, Polygon
  • 色を試す
    • display = doの内容を書き換えればいろいろできる
import Graphics.UI.GLUT
 
myK = 12

myPoints :: [(GLfloat,GLfloat,GLfloat)]
--myPoints = [ (sin (2*pi*k/12), cos (2*pi*k/12), 0) | k <- [1..12] ]
myPoints = [ (sin (2*pi*k/myK), cos (2*pi*k/myK), 0) | k <- [1..myK] ]
 
main :: IO ()
main = do
  (_progName, _args) <- getArgsAndInitialize
  _window <- createWindow "Hello World"
  displayCallback $= display
  mainLoop
 
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
import Graphics.UI.GLUT
import Bindings
 
main :: IO ()
main = do
  (_progName, _args) <- getArgsAndInitialize
  _window <- createWindow "Hello World"
  displayCallback $= display
  reshapeCallback $= Just reshape
  keyboardMouseCallback $= Just keyboardMouse
  mainLoop
    • import Bindingsがなされている
    • これは以下のような作成モジュールなので、stack new h170505して作った環境では、.../h170505/src/以下にBindings.hsとして配置する必要がある
module Bindings (display, reshape, keyboardMouse) where
 
import Graphics.UI.GLUT
import Display
 
reshape :: ReshapeCallback
reshape size = do 
  viewport $= (Position 0 0, size)
 
keyboardMouse :: KeyboardMouseCallback
keyboardMouse _key _state _modifiers _position = return ()
    • このようにして置いたBindings.hsであるが、これだけでは、コンパイルしようとしても、見つからないので、h170505.cabalに書き込む。書き込む先は Library情報の場所。
    • また、Bindings.hsでもGLUTをimportしている。GLUTをMain.hsで使うときに.cabalファイルを書き換えたが、Libraryのファイルの方にもこれを使うときには、そのことを別途.cabalに指定する
library
  hs-source-dirs:      src
  exposed-modules:     Lib
                     , Bindings
  build-depends:       base >= 4.7 && < 5
                     , GLUT
                     , OpenGL
  default-language:    Haskell2010
    • さらに、Binding.hsの中身を見ると、import Displayしており、これも、以下のようなコードで作成し、src以下に置くので
module Display (display) where
 
import Graphics.UI.GLUT
import Cube
 
display :: DisplayCallback
display = do 
  clear [ColorBuffer]
  cube 0.2
  flush
    • h170505.cabalにはさらに、このDiplayも追記して以下のようにする
library
  hs-source-dirs:      src
  exposed-modules:     Lib
                     , Bindings
                     , Display
  build-depends:       base >= 4.7 && < 5
  default-language:    Haskell2010
library
  hs-source-dirs:      src
  exposed-modules:     Lib
                     , Bindings
  build-depends:       base >= 4.7 && < 5
                     , GLUT
                     , OpenGL
  default-language:    Haskell2010
    • さらに、Diplay.hsを見るとCubeをimportしている。このCube.hsも作るので
module Cube where
 
import Graphics.UI.GLUT
 
cube :: GLfloat -> IO ()
cube w = do
  renderPrimitive Quads $ do
    vertex $ Vertex3 w w w
    vertex $ Vertex3 w w (-w)
    vertex $ Vertex3 w (-w) (-w)
    vertex $ Vertex3 w (-w) w
    vertex $ Vertex3 w w w
    vertex $ Vertex3 w w (-w)
    vertex $ Vertex3 (-w) w (-w)
    vertex $ Vertex3 (-w) w w
    vertex $ Vertex3 w w w
    vertex $ Vertex3 w (-w) w
    vertex $ Vertex3 (-w) (-w) w
    vertex $ Vertex3 (-w) w w
    vertex $ Vertex3 (-w) w w
    vertex $ Vertex3 (-w) w (-w)
    vertex $ Vertex3 (-w) (-w) (-w)
    vertex $ Vertex3 (-w) (-w) w
    vertex $ Vertex3 w (-w) w
    vertex $ Vertex3 w (-w) (-w)
    vertex $ Vertex3 (-w) (-w) (-w)
    vertex $ Vertex3 (-w) (-w) w
    vertex $ Vertex3 w w (-w)
    vertex $ Vertex3 w (-w) (-w)
    vertex $ Vertex3 (-w) (-w) (-w)
    vertex $ Vertex3 (-w) w (-w)
    • .cabalは
library
  hs-source-dirs:      src
  exposed-modules:     Lib
                     , Bindings
                     , Display
                     , Cube
  build-depends:       base >= 4.7 && < 5
                     , GLUT
                     , OpenGL
  default-language:    Haskell2010
    • となる
    • 結局:
    • Main.hsは.../h170505/app/以下にあって
import Graphics.UI.GLUT
import Bindings
 
main :: IO ()
main = do
  (_progName, _args) <- getArgsAndInitialize
  _window <- createWindow "Hello World"
  displayCallback $= display
  reshapeCallback $= Just reshape
  keyboardMouseCallback $= Just keyboardMouse
  mainLoop
    • .../h170505/src/以下には、Bindings.hs,Diplay.hs,Cube.hsがあって
    • h170505.cabalファイルは
name:                h170505
version:             0.1.0.0
-- synopsis:
-- description:
homepage:            https://github.com/githubuser/h170505#readme
license:             BSD3
license-file:        LICENSE
author:              Author name here
maintainer:          example@example.com
copyright:           2017 Author name here
category:            Web
build-type:          Simple
extra-source-files:  README.md
cabal-version:       >=1.10

library
  hs-source-dirs:      src
  exposed-modules:     Lib
                     , Bindings
                     , Display
                     , Cube
  build-depends:       base >= 4.7 && < 5
                     , GLUT
                     , OpenGL
  default-language:    Haskell2010

executable h170505-exe
  hs-source-dirs:      app
  main-is:             Main.hs
  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
  build-depends:       base
                     , h170505
                     , GLUT
                     , OpenGL
  default-language:    Haskell2010

test-suite h170505-test
  type:                exitcode-stdio-1.0
  hs-source-dirs:      test
  main-is:             Spec.hs
  build-depends:       base
                     , h170505
  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
  default-language:    Haskell2010

source-repository head
  type:     git
  location: https://github.com/githubuser/h170505