Java : java.io.File
モナドなんかで遊んでいる場合ではないのだ!
[id:lethevert:20060905:p5]の問題がまだ解決していないんだ!!
Javaが古かったので、最新のjdk1.5に入れ替えたのですが、やっぱりダメでした。むぅ〜。
Concurrent Clean : リストIO
[id:lethevert:20060905:p1]のコメント欄で思いついたモナドをリストに見て、順番に食わせれば・・・というネタを実装してみようと思ったのですが、
よく考えてみると、Haskellのモナドは左結合ですね。リストは右結合なので、出来上がる構造が逆になってしまいます。
でも、あろはさんところで紹介されていた「Monads for Functional Programming (PDF)」を見ると、結合性は本質ではないそうなので、言語設計者の趣味の問題ですかね。しかし、現実のプログラミングでは、そういうところが結構気になったりするものなのですが。
さて、次のようなプログラムを作ってみました。今回はHaskellではなくCleanで。
module ListIO import StdEnv ::Void = Void ::IO a b :== a -> *File -> *(b, *File) ::ListIO a = EndIO | E.b: (>=>) infixr 0 (IO a b) (ListIO b) set a _ f = (a,f) writes a f # f = fwrites a f = (Void,f) writei a f # f = fwritei a f = (Void,f) readi _ f # (_,i,f) = freadi f = (i,f) run = set "Please input integer\n" >=> writes >=> readi >=> writei >=> set "\nend\n" >=> writes >=> EndIO perform :: a (ListIO a) *File -> *File perform _ EndIO f = f perform a (p >=> lio) f # (b,f) = p a f = perform b lio f Start w # (f,w) = stdio w f = perform Void run f (_,w) = fclose f w = w
>=>というデータ構築子を定義して、perform関数で頭から順に実行していくように実装してみました。
ところで、これで、存在型の使い方をまた1つ覚えました。
Haskell : モナド
当分モナドには触れないつもりだったのですが、ちょっと思いついたので、一つ書いて見ます。
もちろん、副作用がない参照透過なHaskellにとっては全く重要ではないことですが
IOを行うモナドをHaskellの自作してみようというネタです。unsafePerformIOという関数を利用していますが、低レベルで副作用を持った外部関数を呼んでいるイメージで。
module MyIO where import Monad import System.IO.Unsafe print2 a = (unsafePerformIO (putStrLn a)) `seq` (A ()) data A a = A a get (A a) = a instance Monad A where --; 1. a >>= f = f (get a) --; 2. a >>= f = a `seq` (f (get a)) --; 3. a >>= f = let b = f (get a) in b `seq` a `seq` b return a = A a start :: () start = let A a = k in a where k = do return () print2 "Hello " print2 "World!"
途中、コメントアウトしている3種類のbindの実装があるのですが、ここの実装の違いがどういう振る舞いの違いを見せるかというところに注目。
1. a >>= f = f (get a)
MyIO> start World! ()
print2関数で引数を使っていないので、bindの引数のaが捨てられてしまって、最後の"World!"のところしか出力されていません。
2. a >>= f = a `seq` (f (get a))
MyIO> start Hello World! ()
そこで、bindの中で強制的に引数のaを評価してやると、正常に出力されるようになりました。
3. a >>= f = let b = f (get a) in b `seq` a `seq` b
MyIO> start World! Hello ()
「a `seq` (f (get a))」の順序を逆にしてやれば、逆順に出力されるbindが作れるのですが、普通に作ると型が合わなくなるので、「b `seq` a `seq` b」のように回りくどいことをやっています。