時間泥棒の夕べ − 排中律と call/cc
![]()
下に書く Haskell のソースでは、直積 A×B は Prod a b と、直和 A+B は Sum a b となっています。Haskell であるデータ c が型 C を持つことは c :: C と記述し、X から Y への関数の型は f :: X->Y と記述します。 任意の A, B という型について、first :: (Prod A B)->A と second :: (Prod A B)->B がすでに定義されていたとします、ある二つの関数 fa :: A->C, fb :: B->C を取ってきたとき、fab :: (Prod A B)->C が常に fab(x::(Prod A B)) = fa(first(x))::C となるのは、結局、この定義と同じ意味を持つもの「唯一つ」になります。実際 Haskell には、 Prod A B と同じ「機能」を持つ Tuple すなわち (A,B) は、常に Prod A B と相互に変換できることになります。
同様なのですが、普段から数学で(無意識に)使う直積に比べ、矢印の方向が逆の直和については、なじみがないかもしれません。アイデアとしては、厳しく型付けする場合、ある型と別の型とが同じデータを持つ可能性があったとき、どちらかの関数を使えと指示するのは危険と見なされるのですが、あらかじめ違う型を表す「ラベル」を先に付けておけば、そもそも違うデータなので安全に扱えるということがあります。このラベルが InLeft と InRight です。Haskell にはこのラベルに関する「パターンマッチ」という機能があり、ga::C->A, gb::C->B とすると、gab(InLeft(x)::(Sum A B)) = ga(x)、gab(InRight(y)::(Sum A B)) = gb(X) と定義すれば、gab は A と B のどちらのデータも扱える関数とほぼなります。Haskell には同じ「機能」を持つものとして、Either A B が定義されていますが、それと Sum A B は常に相互変換ができ区別が付きません。
|
callCC は、call with current-continuation の略です。応用として例外処理などが書けます。プログラムでは関数の引数に別の関数を渡すことがよく行なわれますが、この「関数」としてなんと(C言語などにおける) return を渡せるようにしたのが、この機構と言えます。
記号論理学的に定義を書いた callCC の論文によると、形式的に callCC は、「ある解釈の環境 E[] 下で、M という項が問題になっているとき E[M] と書くとすると、E[call/cc(M)] |-> E[M λz. A(E[z])]。この A は環境を固定化し、A が解釈されそうになると、その内に留めた環境に引き戻す効果をもつ」と、だいたいできそうです。
callCC の型は callCC :: ((a->Cont r b)->Cont r a)->(Cont r a) となります。Haskell ではラムダ抽象 (λx. M[x]) は、慣れないと記号の重なりがややこしいですが、(\x -> M x) と記述します。callCC は (callCC (\k -> return (M k))) という形で利用されることが多いでしょう。何かを取ってそれをそのまま後続に継ぐ return の型は、通常、 a->Cont r a となりますが、callCC の型は、何かを取ってどこかに返すk :: (a->Cont r b) が仮にあるとすると、それを利用した M k が k が取るべき引数と同じ型を持つように M k を作れば、最初 M k を return しますが、k という関数に引数が与えられると、その return を「なかったこと」にして、こんどは k に与えられたはずの引数を return したものとして動作します。
|
後述のウェブページにあった「排中律」の定義を参考にプログラムしています。
{-# LANGUAGE GADTs, RankNTypes, TypeFamilies, ScopedTypeVariables #-}
module Momo where
import System
import Control.Monad.Cont
{- 直和の定義。 -}
data Sum x y = InLeft x | InRight y
{- 直積の定義。 -}
data Prod a b = Prod a b
first :: Prod a b -> a
first (Prod x y) = x
second :: Prod a b -> b
second (Prod x y) = y
{- 「物語」のはじまり。 -}
data Special = SomethingSpecial deriving (Eq, Show)
data Ordinary = SomethingOrdinary deriving (Eq, Show)
{- 「排中律」を gigi という名前で参照します。 -}
gigi :: (forall a b r. Cont r (Sum a (a -> Cont r b)))
gigi = callCC(\k -> return (InRight (\x -> k(InLeft(x)))));
{- liliana は何か普通のものを返します。 -}
liliana :: (forall r. Cont r Ordinary)
liliana = return SomethingOrdinary
{- nino は InLeft のラベルがあれば、特別なものが手に入ったと判断します。
InRight のラベルがあれば、liliana から返されたものを受け取るのみです。
-}
nino :: (forall b r. Cont r String)
nino = gigi >>= \s -> case s of
{
InRight(query) -> let {
experience = "Search Something...\n"
} in liliana >>= (\answer -> return(Prod answer experience))
>>= \(x :: Prod Ordinary String)
-> return (experience
++ "Unhappy End. Got "
++ (show (first x)));
InLeft(x:: Prod Special String) ->
return ((second x) ++ "Happy End. Got " ++ (show (first x)));
}
{- hora は何か特別なものを返します。 -}
hora :: (forall r. Cont r Special)
hora = return SomethingSpecial
{- nicola は nino の「分身」で、ほとんど同じ定義を持っています。
nicola は InLeft のラベルがあれば、特別なものが手に入ったと判断します。
InRight のラベルがあれば、hora から返されたものを受け取って、query
に渡します。
-}
nicola :: (forall r. Cont r String)
nicola = gigi >>= \s -> case s of
{
InRight(query) -> let {
experience = "Search Something...\n"
} in hora >>= (\answer -> query(Prod answer experience))
>>= \(x :: Prod Ordinary String)
-> return (experience
++ "Unhappy End. Got "
++ (show (first x)));
InLeft(x :: Prod Special String) ->
return ((second x) ++ "Happy End. Got " ++ (show (first x)));
}
{- 「継続」を走らせた結果を出力させます -}
main = do
runCont nino putStrLn
runCont nicola putStrLn
以上のソースを momo_0.hs として Haskell のインタプリタ(WIndows では WinGHCi)に渡すと次のような結果が得られます。
Prelude> :load *momo_0 [1 of 1] Compiling Momo ( momo_0.hs, interpreted ) Ok, modules loaded: Momo. *Momo> main Search Something... Unhappy End. Got SomethingOrdinary Search Something... Happy End. Got SomethingSpecial |
大魔王モモ(Momo)は、やんちゃなジジ(gigi)を地上に送ります。ジジは料理店で働きます。 店主はジジに珍宝である秘伝のスパイス(x:Special) があるかと聞きます。ジジはそんなことは知りません。でも夢でモモがそっとささやきました。「ないことにしてあなたが成長すれば、あなたの敵がきっとそれを発明してきます。その世界を苦しむというなら、あなたをそれが今あった世界に導きましょう。」
店主がニノ(nino)さんのとき。
店主がニコラ(nicola)さんのとき。
|
上のプログラムを少し複雑にしてみました。nino の部分が beppo に、 nicola の部分が kassiopeia に変わって、nino の役割が変化したほか、新しい定義もいろいろあります。
{-# LANGUAGE GADTs, RankNTypes, TypeFamilies, ScopedTypeVariables #-}
module Momo where
import System
import Control.Monad.Cont
{- 直和の定義。 -}
data Sum x y = InLeft x | InRight y
{- 直積の定義。 -}
data Prod a b = Prod a b
first :: Prod a b -> a
first (Prod x y) = x
second :: Prod a b -> b
second (Prod x y) = y
{- 「物語」のはじまり。 -}
data Special = SomethingSpecial deriving (Eq, Show)
data Ordinary = SomethingOrdinary deriving (Eq, Show)
{- 「排中律」を gigi という名前で参照します。 -}
gigi :: (forall a b r. Cont r (Sum a (a -> Cont r b)))
gigi = callCC (\k -> return (InRight (\x -> k (InLeft x))));
{- 言葉と普通な何かが Service。言葉と特別な何かが Love と定義します。-}
type Service = Prod Ordinary String
type Love = Prod Special String
{- liliana は何か普通のものを返します。 -}
liliana :: (forall r. Cont r Ordinary)
liliana = return SomethingOrdinary
{- nino は liliana から受け取った dish に spice をかけて
Service を求める claimant に渡します。
-}
nino :: (forall a b r. String->(Prod (Service->Cont r a) b)->Cont r a)
nino spice claimant
= liliana >>= \dish -> ((first claimant) (Prod dish spice))
{- finish は終了時の出力方法を定義します。 -}
finish ishappy experience gotten = let {
dish = first gotten;
spice = second gotten;
happiness = if ishappy then "Happy" else "Unhappy";
} in return (spice ++ experience
++ happiness ++ " End. Got "
++ (show dish) ++ ".");
{- resistant は claim があったとき、proof と訴えを返す方法と
証明すべき truth を組にして準備します。
-}
resistant proof truth
= Prod (\claim -> return (Prod proof (second claim))) truth
{- beppo は InLeft のラベルがあれば、momo が nino の命令で何か特別なも
のを手に入れたと判断します。
InRight のラベルがあれば、liliana から受けとったものへの反応を nino
に渡すのみです。
-}
beppo = \s -> case s of
{
InRight(query) -> let {
experience = "Search Something...\n";
} in liliana >>= (\answer -> return (resistant answer experience))
>>= (nino "Beg to ")
>>= \(g :: Service) -> finish False experience g;
InLeft(momo) -> let {
experience = second momo;
} in (nino "Order to " momo)
>>= \(g :: Love) -> finish True experience g;
}
{- hora は何か特別なものを返します。 -}
hora :: (forall r. Cont r Special)
hora = return SomethingSpecial
{- kassiopeia は InLeft のラベルがあれば、momo が nino の命令で何か特
別なものを手に入れたと判断します。
InRight のラベルがあれば、hora から受けとったものを query してから
その反応を nino に渡すのみです。
-}
kassiopeia = \s -> case s of
{
InRight(query) -> let {
experience = "Search Something...\n";
} in hora >>= (\answer -> query (resistant answer experience))
>>= (nino "Beg to ")
>>= \(g :: Service) -> finish False experience g;
InLeft(momo) -> let {
experience = second momo;
} in (nino "Order to " momo)
>>= \(g :: Love) -> finish True experience g;
}
main = do
runCont (gigi >>= beppo) putStrLn
runCont (gigi >>= kassiopeia) putStrLn
以上のソースを momo.hs として Haskell のインタプリタ(Windows では WinGHCi)に渡すと次のような結果が得られます。
Prelude> :load *momo [1 of 1] Compiling Momo ( momo.hs, interpreted ) Ok, modules loaded: Momo. *Momo> main Beg to Search Something... Unhappy End. Got SomethingOrdinary. Order to Search Something... Happy End. Got SomethingSpecial. |
| ■ |
あとがき
|
| ■ |
関連
|
|
| 更新: | 2011-01-15--2011-01-20 |
| 初公開: | 2011年01月20日 19:41:30 |
| 最新版: | 2012年01月24日 20:23:24 |
2011-01-20 19:41:29 (JST) in ストーリー, 情報工学・コンピュータ科学, 精神分裂病, 論理学 | 固定リンク | コメント (3) | トラックバック (6)
「ストーリー」カテゴリ内の最近の記事
「情報工学・コンピュータ科学」カテゴリ内の最近の記事
「精神分裂病」カテゴリ内の最近の記事
「論理学」カテゴリ内の最近の記事
批評や挨拶のためのネットコミュニティ
- はてなブックマーク(って何?)
トラックバック
この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/93568/50638687
他サイトなどからこの記事に自薦された関連記事(トラックバック)の一覧です。
» JRF の私見:雑記:時間泥棒の夕べ − 排中律と call/cc (この記事)
本稿では、callCC を使ってループを作り、まるで goto のように見えるような動作を構成してみる。Haskell は、強く型付けされた関数型言語だが、十分実用的であって、無限ループも普通に書ける。goto が作れたからと言ってうれしいことはないかもしれないが、その動作がおよそ非直観的で興味深く、これはぜひ紹介したいと考えたのが、本稿を書いた動機である。... 続きを読む
受信: 2011-01-20 19:48:32 (JST)
藤丸です。サントリフラワーズという会社がございます。日本の園芸会社のひとつで、あのサントリーの花部門が独立して設立された会社だそうです。そしてその... 続きを読む
受信: 2011-01-20 20:46:21 (JST)
毎日更新、よく当たる為替レート予想のブログ http://sikyoufxbloggers.blog2.fc2.com?kakiko 【サルでも勝てる無料FX必勝法】 2011年FX必勝法・攻略法・裏技大公開! http://www.sikyou.com/main/trade/fx_waza.html?kakiko 【メールマガジン】 勝率8割以上!毎日配信、よく当たる為替レート予想 読者数 4,500人突破! http://www.mag2... 続きを読む
受信: 2011-01-21 10:32:14 (JST)
Apple TVを購入… いうセリフは ある一定の年齢以上の方であれば 懐かしく感じることでしょう。 奇しくもその数年後に高校の数学の授業で 背理法 という証明法を教わったときにまさにこのCMの 「ハイリホー」 のフレーズを思い出し、仲間内で爆笑したコトを思い出しました。もう四半世紀(続きを読む) 1/14おしまい . だれが、ちょっかくって きめたの!?(一同、失笑) 森木さんから藤本さんへ 第2問目の疑問は 「なぜ、僕は男の子なの?」 “背理法"は アホな私でも分かりやすかった。 森木さんは難し... 続きを読む
受信: 2011-02-08 08:40:00 (JST)
時間泥棒の夕べ [情報工学・コンピュータ科学,箴言・辛言・戯... が書いているのに間違いはない。 興に入って脱線が過ぎたようだ。本題に復帰しよう。排中律が可能ならば背理法・二重否定の除去もできないはずはなかろう。ならば、次のような「行ったり来たり」も可能である。 上のプログラムを 【社内勉強会】年金財源と日銀の国債直接引受 局面」 )にあっては、デフレギャップがわが国の場合膨大ですので、一定期間 ある種の「無税国家」 (米FRB総裁 「バーナンキ総裁の背理法」 と呼ばれるもの)を実現できるので... 続きを読む
受信: 2011-02-19 05:34:33 (JST)
ゆるぎない明確なロジックによる裁量トレード手法及び自動売買ソフトウェア 続きを読む
受信: 2011-06-17 08:30:05 (JST)

コメント
momo_hs.shar 内のファイルの gigi の上に the law of excluded middle とコメントで書いおいた。実質何も変わっていない。コメント以外の差がないので混乱を避けるために以前のバージョンへのリンクは書きませんが、もし以前のバージョンが必要でしたら、別名でネットに残してありますので、お申し付けください。
投稿: JRF | 2011-01-21 16:19:44 (JST)
Some time ago, I needed to buy a good house for my business but I did not earn enough money and could not purchase something. Thank goodness my sister suggested to try to get the mortgage loans from reliable creditors. Hence, I acted that and used to be satisfied with my car loan.
投稿: loan | 2011-11-27 21:31:19 (JST)
投稿: JRF | 2012-01-24 20:28:03 (JST)