State パターンっていうの?ああいうやつ?

2ch にこんなんあったよ。

オブジェクトを使わずにステートマシンを作るのによい方法はありますか?

勉強のためにStateパターンをモジュールを使ってやってみようと思ったのですが、
相互依存を回避するうまい方法が思いつきません。
また、状態をそれぞれ別モジュールにするにせよ、一箇所にまとめるにせよ、動的に切り替えるためには
結局パターンマッチさせてそれらを呼び出すようになると思いますが、もっとスマートな方法はありますか?

私はデザインパターン何それ美味しいのという人なので、まずよく判りませんでした。

流れ:
キャラクタがバイトをして財布がいっぱいになったら銀行へ行く
ということを目標金額まで繰り返し、到達したらその金がなくなるまで
家でごろ寝し、なくなったらまた働く

キャラクタは
  ・バイトをする : 手持ちが1増える
  ・銀行で貯金をする : 手持ちを0にし、貯金が1増える
  ・家で寝る : 貯金が1減る
の状態を取ります。
財布の許容量は3、目標貯金額は5とします。

ああそうですか。で、パターンマッチいやなの。じゃあこんなのどうですか:

module Human = struct
  type t = {
    pocket : int;
    bank : int;
  }
  let zero = { pocket = 0; bank = 0 }
end

open Human

module Action = struct
  let work h =
    prerr_endline "worked";
    let pocket = h.pocket + 1 in
    assert (pocket <= 3);
    { h with pocket = pocket }
    
  let deposit h =
    prerr_endline "deposited";
    { pocket = 0; bank = h.bank + 1 }

  let sleep h =
    prerr_endline "slept";
    let bank = h.bank - 1 in
    assert (bank >= 0);
    { h with bank = bank }
end

type t = {
  human : Human.t;
  think : Human.t -> t;
}
    
module type Strategy = sig val init : t end

module Bitekun : Strategy = struct
  let rec work h =
    let h = Action.work h in
    { human = h;
      think = if h.pocket >= 3 then deposite else work }

  and deposite h =
    let h = Action.deposit h in
    { human = h;
      think = if h.bank >= 5 then sleep else work }

  and sleep h =
    let h = Action.sleep h in
    { human = h;
      think = if h.bank = 0 then work else sleep }

  let init = { human = Human.zero; think = work }
end
  
module Workerhoric : Strategy = struct
  let rec work h =
    let h = Action.work h in
    { human = h;
      think = if h.pocket >= 3 then deposite else work }
      
  and deposite h =
    { human = h;
      think = work }

  let init = { human = Human.zero; think = work }
end

module Neet : Strategy = struct
  let rec sleep h =
    let h = Action.sleep in
    { human = h;
      think = sleep }
      
  let init = { human = Human.zero; think = sleep }
end
    
let think : t -> t = fun t -> t.think t.human

let _ = let rec loop t = loop (think t) in loop Bitekun.init

まあ、状態を variant じゃないて closure にしただけだけど、ありがちなパタンマッチは無くなりました。ただこれだと各状態遷移表が mutual recursive functions で表されるから、そこを別モジュールに切っていくって訳にはいかないね。Functor で parametrize して最後に recursive module で合体させればいいか。