週に一回は書きますよ 月に4つ記事を書けばノルマは満たされます。
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Monadiusのメモリリークは簡単な方法では解決しませんでした。GlobalVariablesなどの変数を正則判定してみたり、ending の前にMonadiusを初期化してみたりしましたが効果なし。まじめにリークを探す必要がありそうです。

とりあえずupdateMonadius内にSCCタグを付けまくる作業開始。この関数がひどいという話を前にも書きました

スポンサーサイト

Monadiusがメモリリークしていると聞いたので調べてみました。

  • 音が出ない版をダウンロード
  • 展開
  • .hsが全て小文字で始まっているので、それを大文字にリネーム
  • Main.hsのcreateWindowの前の行にgetArgsAndInitializeを足す。
    • glutを初期化してなかった!
  • $ ghc --make -O -prof -auto-all Main.hs
    • 次のエラーが出た場合、libghc6-glut-profパッケージ等プロファイラ用のパッケージを導入する。
Game.hs:5:7:
    Could not find module `Graphics.UI.GLUT.Callbacks.Window':
      Perhaps you haven't installed the profiling libraries for package GLUT-2.1.1.1? 
      Use -v to see a list of the files searched for.

多分データ型がlazyで、それが評価されるまで前のフレームのデータがずっと保持されているのではないかと当て推量しています。どうももっと重篤なバグで不要なオブジェクトが出来ている気もしますが。

プロファイリングオプションの説明が全く理解できなかったので、全部試してみました。画像が多いので全て続きに収容。

またカリフォルニアの中にいます。

飛行機の中でhgusのソースを眺めていたら、データ型をあつかっている部分があまりに酷いので驚いてしまいました。当日、ラムダを実装し終ってから突貫工事でつけたのでさもありなんといったところですが。まず判定がザルです。正直なにも判定していないので、想定された型とちがう型をもつ引数をいれてもいいです。それから引数の数はチェックしている場所としていない場所があります。これにひっかかるプログラムはどこで異常停止するのでしょうか。

ところでこの場合、型宣言の意味はあるのでしょうか。静的型がない場合にはデータ型は(list 'symbol data1 data2...)ぐらいの意味しかもたないような気がしています。


I'm in Mountain View, CA again.

During the flight, I was checking the source file of 'hgus', the Haskell interpreter. I got really surprised at the part which handles data declaration. It checks nothing. It doesn't check the type of the parameters at all, it seldom checks the number of the parameters.

I suspect that the data declaration declares nothing in this case. Without static type checking, user generated data is just a list which starts with a symbol followed by arguments.

A 120 0 (B hoge)  ===  ('A 120 0 ('B hoge ))

今まで、seqAllを次のように使えるものとして実装しようとしていました。valueが正則評価すべき値、Typeがその値の型です。

$(seqAll $ reifyType Type) value others

この実装はおかしいです。また、Template Haskellを使えば実現できると思っていましたが、それも間違いで、実現不可能です。

  1. valueの型を明示的に指定するのは辛すぎます。複雑な型のコンポーネントすべてを強制的に評価するところが売りの関数を動かすのに、複雑な型を正確に指定しろとかおかしいです。
  2. Template Haskell のreify*の引数は直書きした型とかData型の名前しか許されません。map reifyDecl (Dataのリスト)のようなことはできず、reifyDecl Treeと書くことしかできません。

やはり型クラスを基礎に書いたほうがよさそうです。ちょっと考え中。

Template Haskellを調べてみました。直前のseqAllに使えるかと思いまして。

Template HaskellはHaskell用のテンプレートプログラミングあるいはマクロです。Haskellのプログラム中に「Haskellコードを生成するHaskellコード」を貼り付けてコンパイル時に実行します。lisp/schemeのマクロとかc++ templatesと同じのりです。lispのマクロがc++のtemplatesでないように、Template Haskellもそれらとは結構違いますが。

Template Haskellを使えばたとえば次のコードがかけます。

  • n-タプルのm番目を選択する関数sel n m。$(sel 1 3) (x, y, z) == x
  • zip, zip3, zip4などを統一的にできるzipN。$(zipN 3) == zip3
  • 上のzipNを1からNまで一度に宣言するgenZipN。

上のコード例にあるように、Template Haskellの部分は$(..)で囲まれています。この部分がコンパイル時に評価され、その結果のHaskellプログラムがここに挿入されます。

  • 引数によって型が変わる関数が定義できる。上の例を参照。
  • Template Haskellによって生成されたコードにはちゃんと型検査が行われる。 $(sel 1 3) (x, y)と書くと型エラーになる。
  • 引数に変数は入れられない。\x -> $(sel x 3)なんてコードはかけない。

とりあえずちょっと地味な例として、template haskellで書いたprintf関数が論文の頭に載っています。haskellにはすでにText.Printfモジュールにprintf関数を持っているので、それとの比較をしてみます。

まずは普通のprintfを使ったコードから。

import Text.Printf
main :: IO ()
main = do
    let x :: Int
        x = 3
        y :: Int
        y = 4
    putStrLn "trial: 1"
    printf "variable %s = %d\n" "x" x
    putStrLn "trial: 2"
    printf "variable %s = %s\n" "y" y

実行してみます。

>ghc --make NormPrintf.hs
Linking NormPrintf.exe ...

>NormPrintf.exe
trial: 1
variable x = 3
trial: 2
NormPrintf.exe: Printf.printf: bad argument
次にTemplate Haskellで書いたPrintfを使ってみます。
import ThPrintf (printf) -- definition of printf.
main :: IO ()
main = do
    let x :: Int
        x = 3
        y :: Int
        y = 4
    putStrLn "trial: 1"
    putStrLn ( $(printf "variable %s = %d") "x" x)
    -- putStrLn "trial: 2"
    -- putStrLn ( $(printf "variable %s = %s\n") "y" y)
実行してみます。ghcでは、コンパイラオプションに -XTemplateHaskellをつけるとTemplate Haskellが有効になります。
>ghc --make -XTemplateHaskell TPrintf.hs
[2 of 2] Compiling Main             ( TPrintf.hs, TPrintf.o )
Loading package base ... linking ... done.
Loading package array-0.1.0.0 ... linking ... done.
Loading package packedstring-0.1.0.0 ... linking ... done.
Loading package containers-0.1.0.0 ... linking ... done.
Loading package pretty-1.0.0.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
Linking TPrintf.exe ...

>TPrintf.exe
trial: 1
variable x = 3
ちなみに、ソースの中のコメントアウトを除くと、次のようにコンパイルエラーになります。
>ghc --make -XTemplateHaskell TPrintf.hs
[2 of 2] Compiling Main             ( TPrintf.hs, TPrintf.o )
Loading package base ... linking ... done.
Loading package array-0.1.0.0 ... linking ... done.
Loading package packedstring-0.1.0.0 ... linking ... done.
Loading package containers-0.1.0.0 ... linking ... done.
Loading package pretty-1.0.0.0 ... linking ... done.
Loading package template-haskell ... linking ... done.

TPrintf.hs:12:47:
    Couldn't match expected type `[Char]' against inferred type `Int'
    In the second argument of `$(printf "variable %s = %s\n")', namely
        `y'
    In the first argument of `putStrLn', namely
        `($(printf "variable %s = %s\n") "y" y)'
    In the expression: putStrLn ($(printf "variable %s = %s\n") "y" y)

まず、Template Haskellのprintfだと、引数を間違えると型エラーが生じます。標準のprintfは実行時までエラーがわかりません。これは便利。その一方で、printfのフォーマットを動的に変えたりすることができなくなります。

Template Haskellの便利さがわかりましたか?私にはまったくわかりません。そもそもすでにあるprintfを作ってもまったくアピールになりません。もっとえぐいものが必要です。

使用したOSはWinXPSP2Home, コンパイラはGHC 6.8.1でした。

printfのソースを続きに貼ります。元論文にこれの作りかけのものが貼ってあります。

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。