岩崎さん

向井といいます。


まずは問題点について説明しましょう。そのあとで個人的な意見を述べます。

まず Haskell は遅延評価言語だということを考えてください。このため、ふ
つうの関数も State も遅延評価されます。loop では、引数の cs はいつまで
たっても必要とされません。問題が発生するのはこのためです。

イメージとしては、

  loop []
  loop (update [])
  loop (update (update []))
   :

のように展開されていくと考えてください。これでは停止せず、 loop の引数
がどんどん巨大になっていきますから、そのうち処理系が落ちます。

State の方も似たような感じで、状態そのものが必要とされないので update
がどんどん積まれていくのを想定すれば、おおむねあっていると思います。

この問題を回避する方法としては、 seq を使うというのが、ひとまずあるで
しょう。

loop cs = sum cs' `seq` loop cs'
  where cs' = update cs

loop' = do cs <- gets update
           put cs
           sum cs `seq` loop'


seq は、第一引数を評価してから第二引数を返すという関数です。またここで 
sum としているのは、かりに cs' `seq` loop とすると「cs' がリストである」
という構成子(:)だけを見て終わってしまうためです。最後まで見るという点
では、 last や length を使うという手もあるのですが、各要素に何が入って
いるか、という点がわかりません。ですから、各要素への更新が積まれていく
ことになります。
そこで、各要素の総和である sum をさしあたって採用しています。ま、あく
までもサンプルですが。



さて、本題の「状態を持つ計算」ですが、かならずしも State を使うのが適
切とは限りません。 Data.IORef という「更新可能な変数」を使う方法もあり
ます。あるいは STM を使う手があります(HAppS というウェブアプリケーショ
ンフレームワークは STM をヘビーに使っているという話がありました。詳し
くは知りませんが)。
あとは HashTable を使うという手もありますね。


また、現実的にはゲームの状態は環境とのやりとりを含むので(loop のように
停止しない処理には意味がないので)、最終的には StateT をIO で使うことに
なると思います。これならこれで、 IO になるので上手く行くかもしれません。


個人的なオススメとしては、まずは IORef を使ってみると良いと思います。

-- 
向井 淳
mukai / jmuk.org
http://www.jmuk.org/d/

--
ML: haskell-jp / quickml.com
使い方: http://QuickML.com/