fixIO

上のcatは、ループをするのにcat_mainという関数を用意しなくてはならず、まあ再帰のみでループを実現しようとすると、この手の問題は付き物だけど(特に気にもならない)、不動点コンビネータがあれば、回避できる。Haskellでは、不動点コンビネータMonad版があり、fix::(a->a)->aなのに対して、fixM::(a -> m a) -> m aという型。mが恒等モナドならば、fixM=fixなので、これはfixの一般化になっている

IOモナドの場合は、fixIOが組み込み(IORef使って定義できるらしいけど)。fixMaybeは
fixMaybe f = let r = f (fromJust r) in r
とか、そんな定義。上のcatをfixIOで書き直すと

import System.IO
import Monad

cat fname =
 (openFile fname ReadMode)
 >>=(\hdl ->
     (join
      (ap
       (fixIO
        (\f ->
         (return
          (\h ->
           (hIsEOF h)
            >>=(\eof -> if eof
	                then return ()
	                else (hGetLine h)>>=putStrLn>>(f h))))))
       (return hdl)))>>(hClose hdl))

意味分かりません!Lisp並に深いネスト。

cat_main =
 (fixIO
  (\f ->
     (return
       (\h ->
         (hIsEOF h)
          >>=(\eof -> if eof
		      then return ()
	              else (hGetLine h)>>=putStrLn>>(f h))))))

と置けば多分分かりやすい。いや、分かりにくいよ!cat_main::IO (Handle -> IO ())。Handle -> IO ()でないのは、fixIOの返り値が、IO xxでないといけないので、それに合わせるため(ここでかなりハマった)。apは、"m (a -> b) -> m a -> m b"という型の関数。一生役に立たないと思うけど、monadic fixpointに慣れた。