チートシート
頑張って書きましょう。
注意すべき点
- Haskellでは、関数に対して、あとから引数を増やしたくなった場合は、以下のようにするのが効率が良いっぽい。
- 既にある元の関数とは違う名前の関数を作る(元がhogeならhoge2とか)。
- 新しい関数の「第一引数の前」に、新しい引数を挿入する。
- 元の関数を呼び出している箇所を見付けたら、カリー化して新しい関数を呼ぶように直していく。
- 他の言語のように、ただ単に引数を変更するだけでは、関数の厳密な型チェックに引っかかりまくって、異様に手間がかかってしまう。
- または、最初から引数が増える可能性が分かっている場合は、引数をひとまとめのタプル等にしておき、そのタプルの型などを別定義しておけば、タプルの方をいじるだけで済む。
- この辺のやり方は、以下を参考にした。
- SDLのような外部に実体を持つような存在の場合、Haskell側からは今後もう確実に使われないように見えても、SDL内部で必要とされているような場合があり、そのようなオブジェクトがGCによって破棄されてしまうと、SDL側で例外を出して死んでしまう。
- 対処としては、永続的に存在してほしいオブジェクトでも、きちんと破棄処理をどこかに書いておく事で、「破棄処理のところでまだオブジェクトが要求されるから、勝手に捨てちゃ駄目」という感じで、コンパイラに伝えられる。
- または、「Foreign.ConcurrentにあるaddForeignPtrFinalizer」と、「Foreign.ForeignPtrにあるtouchForeignPtr」を使う事で、二つの外部ポインタ同士の依存関係を設定できるらしい。
- ghcが生成するバイナリはシンボルだらけなので、gdbとか使わないならstripをかけるのがベター。バイナリサイズが半分ぐらいに減る。
リスト操作
- carはhead、cdrはtail。
- 空リストは[]。中身入りリストは[1, 2, 3, 4, 5]。中身の型は揃える必要有り。
- [1,2,3,]のような指定は不可。export対象のところではコンマ終わりを許可してるのにリストのところでは許可しないとか、どうも中途半端な気がする。
- consは中置記法の「:」。行儀の悪いリストは作れない事に注意(多分、Haskellの型制約の為)。
- appendは中置記法の「++」。
- null?は「null」。
Haskellを使ってみた感想
あとでまとめ直す。
- これは非常に良いものだ。
- C(及び、Cの代替にはなれなかったC++やJava)のより良い代替として、最高クラスの記述の楽さと動作の確かさがある。
- 噂に違わず、最強の静的型言語はHaskellではないかと思えるようになった。
- これからは、Cで何か書くような場面では、迷わずCの代わりにHaskellを使いたい。
- 問題は、ghc自体がgcc程にははびこっているとは言い難い事だが……自分でインストールすればいいんだが。
- FFIが標準で入ってるので、楽にCの外部ライブラリを使えるのも良い。
- モジュールのインストールが完全には自動化されてないのがちょっと微妙。windowsだからか。
- プログラマ自身が一体どんなものを作るのか、明確な仕様を持っていない時(要はコード上で試行錯誤したりするような時)は、結構書きづらい。
- 逆に、仕様が決まっているなら、非常に素早く着実に書く事が出来る。
- ゲーム作成等は試行錯誤する部分が大きいと思われるので、直接的にはゲーム作成には不向きっぽい。
- 外回りのエンジンや制御部分等はHaskellでキッチリと書いて、ゲーム内容の本体には別種のスクリプティングエンジンを採用したりするのが良さそう。
- haskell.vimが微妙に使い勝手がよくない。結構ストレスがたまる。
- インデント戻し(デデント?)が丁度良いポイントに戻ってくれない。
- 「=」による自動インデントが機能しない。
- それ以外はまあまあ。
- パターンマッチングによる引数展開とかはgood。
- 気に入らない構文パターンが一部ある。
- doブロック内ではletのinは無くてもいいとか……doブロックが特殊なのは確かに分かるけど、どうしても、それなんてバッドノウハウ?と思ってしまう。
- 自然数型(ループしない、unsignedなInteger)が欲しくなる。
- 実装を考えると微妙にパフォーマンス悪くなりそうだが、純粋関数型言語にはなんとなくこれが欲しい。
- これがあれば、0から始まる数学的帰納法的関数でもマイナス値の事を考えずにすむ。
- Boundedを使えば簡単に作れる?
- Haskellに挑戦する際は、Lispを知ってる方が有利な気がする。
- リスト操作回りはLispの(lazy)listそのまま。そして繰り返し構文は(再帰を除けば)foldやmap系の、リスト由来のものしかないっぽいので、リスト操作の頻度が結構高い気がする。
- しかし、別にその為だけにLispへとまわり道する程のものでも無いとは思うが。
- 中置演算子(またはそれに類する構文糖)は、優先順位がある。
- 中置演算子の優先順位を憶えなくてはならないのはバッドノウハウの中でもかなりバッド度が高い方だ。要はそんなの憶えたくない。
- よって、括弧だらけになる。
- ならば、Lispでよいのでは?と自分は思ってしまう(が、そう思わない人も多いようだ。そういう人は意味の無い優先順位を暗記でも何でもすればいい)。
- だから、個人的には、Liskellへの期待は大きい(でも日本語の解説とか無いのでまだ手は出ない)。
- とは言え、Haskellで憶える必要のある優先順位はそれほど多くないので、まだマシな方だ。「$」構文は結構分かりやすい。
- Haskellだけに限らず、(また、Haskellは他の言語と比べるとマシな方だが、)構文を憶えさせないでほしい。構文を憶える事は、全く意味が無い訳ではないが、本質的には意味が無い。S式でいい。
- 「and」と「&&」の優先度が違うとかいう某言語はもうやめてほしい。そんな事して何のメリットがあるっていうんだ。デメリットの方が明らかに大きい。誰もが間違う。そして間違っている事に気付きにくい。そして、気付いた時にはもう遅い。「憶えれば大丈夫」だって?そんなの憶えたくねー!
- 括弧の意味が複数ある。
- 演算の優先順位を決める「(hoge moge ...)」と、タプルを示す「(111, "2", ...)」や、ブロックとしての「{aaa; bbb}」と、フィールド定義の「{ccc=ddd, eee=fff}」等。
- 用途が違うので慣れれば混乱はしないが、始めたばかりの時は一目見て判断しづらい。
- トップレベルの関数定義では、「イコールの左辺の値から、イコールの右辺の値を求める」という感じの意味になりがちだが、let式の部分では引数マッチングのせいで、「イコールの右辺の値から、イコールの左辺の値を求める」という感じの意味になりがちな気がする。要は、方向が逆なので混乱しやすい。
- 大元の意味論的には、どちらの場合も、式の一番左にある束縛対象についての定義をしているだけだというのは確かに分かるものの……。
- Cのポインタのリファレンス/デリファレンスの混乱を思い出した。
- enum風の「data Hoge = Aaa | Bbb | Ccc」という定義を「data」で行うのが納得行かない。
最終更新 : 2007/11/15 07:02:33 JST