Haskell's ability to work with IO actions as with any other (functional and non-functional) values allows us to define control structures of arbitrary complexity. Try, for example, to define a control structure that repeats an action until it returns the 'False' result:
while :: IO Bool -> IO ()
while action = ???Most programming languages don't allow you to define control structures at all, and those that do often require you to use a macro-expansion system. In Haskell, control structures are just trivial functions anyone can write.
(私訳)
HaskellのIOアクションは、(関数型か否かに関わらず、)他のどんな値とも同様に扱うことができ、任意の複雑な制御構造を定義することができます。試しに、値がFalseを返すまで繰り返す制御構造を定義しみてごらんなさい。
while :: IO Bool -> IO ()
while action = ???多くのプログラミング言語では、制御構造を定義することが全くできないか、マクロ拡張システムを必要とします。Haskellでは、制御構造は誰もが書けるただの関数にすぎません。
とあるのでやってみた。conditionとactionを分けてみました。
import Data.IORef while :: IO Bool -> IO a -> IO () while cond action = do c <- cond if (c) then do action while cond action else return() main :: IO () main = do i <- newIORef 0 while (do { n<-readIORef i; return (n<10) }) (do n<-readIORef i modifyIORef i (+1) print $ "hello"++show n)
「制御構造」に見えるかな。
whileに与えるところでdoを使うのが違和感があるので、使わないようにするとこうなる。
import Data.IORef while :: IO Bool -> IO a -> IO () while cond action = do c <- cond if (c) then do action while cond action else return() main :: IO () main = do i <- newIORef 0 while (readIORef i >>= (\n -> return (n<10) )) (do n <- readIORef i print $ "hello"++show n modifyIORef i (+1))
Groovyならクロージャを渡すようなことで実現できますが、上ではクロージャを全く使ってないところが興味深いです。(lambda式は>>=に渡すために使っているだけ)。
whileに渡したりwhileから返されたりするIOの値に含まれるRealWorld値(モナドによって隠蔽されている)は、それを導くのに使った計算に紐付いて来ていて、そのRealWorld値がmainのdoの地でも受け渡されていることで、mainの結果の計算に関与することになり、mainから始まっている命令的実行に組み込まれるようになるのだな…というイメージがないと黒魔術としか思えないです。
RealWorldによる値の制御は、プログラム上の表記位置とは全く独立なGotoみたいなもの。
グレッグ・イーガンの「順列都市」に出てきた「塵理論」を思い出させる、、って誰か指摘してるかな。