夢想

先日、『高木浩光氏のWinnyへの怨念 - よくわかりません』、『「高木浩光氏のWinnyへの怨念」をちょっと整理 - よくわかりません』と、高木氏のWinnyに対する態度を非難するエントリを書いたが、昨日よいタイミングで高木氏自身がこれらをちょうど払拭する内容のエントリを公開した。

あり得ないことだが、せっかくなので、自分が悪役になったことで間接的に少しでも世のためになったと妄想して、気分よく寝ることにする。

「高木浩光氏のWinnyへの怨念」をちょっと整理

高木浩光氏のWinnyへの怨念 - よくわかりませんが、あまりに適当で曖昧ぐちゃぐちゃだったので普通の文にしてみる。


高木氏と自分で共通していると思う認識

  • Winnyキンタマウィルスなどで意図せぬ情報が流出すると削除不可能となる。これは危険。


これについての、高木氏の態度

  • Winnyヤバイ → Winny根絶すべき → Winnyは絶対悪 → Winnyに肯定的なことを言う奴は馬鹿・カス・悪党・氏ね


かくて、氏は、セキュリティと関係あること無いこと(例えば著作権侵害とか)何でも総動員して、Winnyを叩き、Winnyを肯定またはWinnyの否定に疑義を挟むものを断ずる。


高木氏のコレまでの活動は、「有害な技術的な誤り」の糾弾であり、それは氏の技術的知識に裏打ちされた、確実に「正しい」ものだった。


しかし、Winnyに関しては違う。Winnyに関する危険は「正しい」事実であっても、モノの価値付けは、技術的な正誤とはまったく別のモノだ。Winnyの「価値付け」にはそのような正しさというものはそもそも存在しない。


そして、その「価値付け」を、「高木先生の仰ること」として、技術的正誤と同様に「正しい」として無分別に受け入れる人達。


彼のセキュリティ意識へのこれまでの貢献は計り知れないが、彼は神様でも正義の味方でもない。正しい技術的知識と煽りキャラがヒットした、おもしろおじさんに過ぎない事に十分留意する必要があると思う。


あと、おまけとして。言わずもがなの当たり前だが、セキュリティのためにシステムがあるのではなく、システムが正しく価値を発揮するためにセキュリティも必要とされる。専門家になると自分の分野の観点での重み付けが、モノに対する評価で過剰に重み付けされやすい点も留意が必要だと思う。

      • -

↓元エントリに書いてた追記記事。

2007年06月09日 HiromitsuTakagi まだこんな奴がいるぜ。

本エントリに対する、氏自身のはてブコメント。コレの意味を、仮に「(Winnyは駆逐すべき絶対悪に決まっているのに)まだこんな(Winnyの否定にいちゃもんをつける)奴がいるぜ」という意味に解釈すると、「「nyは絶対悪」の結論ありき」という本エントリの趣旨はハズしたものでもないと思うわけですが、なにせ短くて真意を汲む自信はありません。


自分の、氏のWinny撲滅活動の認識は、

彼はセキュリティ面でWinnyの危険性に警鐘を鳴らしていたはず。それが危険性を声高に叫んでいる内にいつのまにかWinny憎しとなり、Winnyを否定するためには手段を選ばない論法になりつつあるような気がする。

http://www.asks.jp/users/mechag/40697.html

というモノにかなり近い*1。氏が煽りキャラとかそんな事はどうでもいい。セキュリティの問題と著作権の問題は別の問題。「著作権侵害になるからヤバイよ」とのお為ごかしは、氏にとっては「怨念」を晴らす手段でしかない。その怨念で色んなモノが見えなくなってはいませんか?、と。まあそう言うようなことを言いたい。

*1:引用元のエントリに全面賛成というわけではない

高木浩光氏のWinnyへの怨念

前から思ってたけど、なんなんだろう、この人の尋常ならざるWinny*1への憎しみは。ときおり根拠らしく述べる内容は、どう見ても「nyは絶対悪」の結論ありきの後付的な臭いがする物ばかり。


単なるアスペ性の粘着(によるバランス欠如)なのか。単なる成人型中二病なのか。


「病気」かどうかはどうでもいい。実際その症状は、それは日本におけるセキュリティの意識向上に相当貢献してる事は間違いないだろう。でも、それはやはりたまたまなのか。反逆の正義ネタとしてセキュリティに粘着してたら、それがたまたまヒットしたと。たまたま社会的にもセキュリティが重要な物になってしまったと。


それとも、本気で社会のためを思うのが先にあって、セキュリティの啓蒙活動をしてきたのだろうか。本気で社会のためを思って、本気で社会の発展の為にこそ、法も憲法も無視してnyの抹殺活動をしているのだろうか。


もちろん、仮にそうならそうで、それはそれでキモいけど。


追記。随分悪意的に見える断片がexcerptで飛んでってしまったなぁ。



★↑があまりに適当で曖昧ぐちゃぐちゃで理解困難なので、少し整理した内容(と追記)を 別エントリ にしました。

.

*1:対象は、nyに限らず、shareとかP2Pの共有ソフト全般みたいだけど

do記法でArrowを使いこなす基本メモ

3分で解るHaskellのArrowの基本メモ - よくわかりませんで残ったdo記法。Arrow syntax7.17. アロー記法を読んで理解したつもりのメモ。


感覚的には、Monadのdo記法と同じ。違いは、

  • Arrowを作る予約語「proc」を先頭で使う。感覚的には「(proc x->○△□)」は、「arr (\x->○△□)」と同じ。
  • 「-<」を使ってArrowへの入力を書く。「a -< x」で、aへxを入力*1
  • Arrowのdo記法は、doブロック全体でまたArrowになる。最後は、Arrowへ何か入力させる形が普通。(何もしない素通しのArrow「returnA」*2がよく使われる)


入力を、Arrow「f」と「g」に食わせたそれぞれの結果を + するArrow 「addA」(イメージ図を見ると解りやすい)

addA f g = f &&& g >>> arr (\ (y, z) -> y + z)

*3

    addA :: Arrow a => a b Int -> a b Int -> a b Int
    addA f g = proc x -> do
                    y <- f -< x
                    z <- g -< x
                    returnA -< y + z

こう書ける。


バラして見てみると、

    addA f g = proc x -> do

「addA f g」は、xを入力として、do以下のコマンドを実行するArrow。

                    y <- f -< x
                    z <- g -< x

Arrow「f」にxを入力した出力をy、Arrow「g」にxを入力した出力をz、として、

                    returnA -< y + z

「y + z」を、何もしない素通しのArrow「returnA」に入力。(というArrowが出来た。returnAの出力をどうするかは、このArrowを使う人次第)



Monadのdo記法と同様、let節(inが無い奴)も使える。


さらに、Arrowの種類(サブクラス)によっては、if節やcase節による分岐、rec節による再帰もできるらしいが、また今度。


(追記)Arrowのdo記法は、Haskell98に対する拡張記法なので、GHCで-farrowsオプションが必要(GHCiのREPL中なら「:set -farrows」)。

*1:「-<」でArrowに入力を指定したもの(例:「a -< x」)を コマンド という

*2:定義は「returnA = arr id」

*3: (&&&)は、(***)の前に「\x->(x,x)」を噛ませたもの

MonadとArrowの関係。

昨日のArrowのエントリ

MonadもArrow(に出来る)…みたいだけど詳しい事は書いてなかった。

と書いたけど、手っ取り早く、GHC6.6.1のControl.Arrowのソースを見てみた。


要は、モナド自体じゃなくて、モナドの>>=の右辺に渡す b -> m c *1な関数の方をArrowにしてしまう、という話だ。

newtype Kleisli m b c = Kleisli { runKleisli :: b -> m c }

instance Monad m => Arrow (Kleisli m) where
	arr f = Kleisli (return . f)
	Kleisli f >>> Kleisli g = Kleisli (\b -> f b >>= g)
	first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))


ただしArrowのカタチの「a b c」に揃えるために、>>= の右辺に渡す b -> m c な関数を、容れ物(Kleisli)に入れる。入れると

Kleisli m b c 

こういうカタチに。「Kleisli m」がArrowの種類の型a、「b」が"引数"の型b、「c」が"返値"の型c。モナドの種類m毎に、Kleisli mなるArrowの種類が出来る。


で、メソッド実装定義を見てみると、

Arrowのからくりである (>>>) と first(並列結合(***)はこれで作れる) の中に、モナドのからくりである (>>=) が仕込まれている。


これによって、 (>>>) が、モナドを (>>=) で繋ぐ役割を果たす。つまり、aをfしてgしてhする

(return a) >>= f >>= g >>= h

が、

runKleisli (arr f >>> Kleisli g >>> Kleisli h) a

になる。


あと、ArrowにはArrowPlus(とArrowZero)ってのがあって、

class Arrow a => ArrowZero a where
	zeroArrow :: a b c
class ArrowZero a => ArrowPlus a where
	(<+>) :: a b c -> a b c -> a b c

やはり、MonadPlusが対応する、と。

instance MonadPlus m => ArrowZero (Kleisli m) where
	zeroArrow = Kleisli (\x -> mzero)
instance MonadPlus m => ArrowPlus (Kleisli m) where
	Kleisli f <+> Kleisli g = Kleisli (\x -> f x `mplus` g x)

誤りなど、乞うご指摘。

*1:普通は a -> m b って書くけど、Arrowのa b cと合わせるため こう書いておく

3分で解るHaskellのArrowの基本メモ

日本語でArrowの説明があまり見付からなかったので、Haskell/Understanding arrows - Wikibooks, open books for an open worldを読んで理解したつもりのメモ。オリジナルの図を見ながらだと分かりやすいかも。誤り・間違いなど、乞うご指摘。


b->cな関数をArrowなるからくり箱に入れると、

 (Arrow a) => a b c

こんな型になる。型パラメタが、引数のbと返値のcのふたつ。

  • Monadは、値をからくり箱に入れたもの。からくり箱は基本的に(ヘンな)値。
  • Arrowは、関数をからくり箱に入れたもの。からくり箱は基本的に(ヘンな)関数。
arr :: (b -> c) -> a b c
  • Monadでは、値をからくり箱に入れる関数returnがある。
  • Arrowでは、関数をからくり箱に入れる関数arrがある。
(>>>) :: a b c -> a c d -> a b d
  • Monadでは、値入りからくり箱を a -> m b な関数を使って繋ぐ (>>=) がある。繋がると全体がまた(ヘンな)値(=からくり箱)
  • Arrowでは、関数入りからくり箱同士を繋ぐ (>>>) がある。繋がると全体がまた(ヘンな)関数(=からくり箱)


要は、Arrowは、関数の「入力(引数)を受け取って出力(返値)を出す」という部分を抽象化したもの。


で、(>>>)が関数の合成、つまり(.)にあたる。

f.g

だと、gしてf(評価の遅延とかおいといて)という当たり前のことしか出来ないけど、

(arr g) >>> (arr f)

だと、概念上はgとfを直列に繋ぐという扱いだけど、>>>の実装でいろんなからくりを仕込むことが出来る。(Monadの>>=の実装がいろんなからくりを仕込んでるように)


Arrowは、この直列連結のほかに並列連結がある。すでにからくり箱に入った関数ふたつを並列に繋ぐクラスメソッドがこれ。

(***) :: a b c -> a b' c' -> a (b, b') (c, c')

外部的には、単に引数をタプルで受けて、返値もタプルにしただけ。元ページの図を見ると概念も分かりやすい。


で、Arrowにもdo記法があって、あと、実はMonadもArrow(に出来る)…みたいだけど詳しい事は書いてなかった。
(これらについて、

に追記)。


追記。最初「(arr f) >>> (arr g)」と書いてたのを「(arr g) >>> (arr f)」に訂正。>>=に使う関数を「a -> m a」と書いてたのを「a -> m b」に訂正。

Haskellの継続モナド(Continuation Monad)を理解するポイント

モナドのすべて継続モナドのところが簡潔で、概念とかもあまり明示的に説明してなくて理解に苦労したので、ポイントをメモ。誤りなど、乞うご指摘。

newtype Cont r a = Cont { runCont :: ((a -> r) -> r) }
instance Monad (Cont r) where 
    return a       = Cont $ \k -> k a
    (Cont c) >>= f = Cont $ \k -> c (\a -> runCont (f a) k)

概要的なこと

継続といいつつ、Contが表すのは継続というより、CPSな関数。*1

  • CPSな関数は、引数に関数をとり、自分の仕事の最後でその関数(継続)を呼ぶ。
  • CPSな関数に、継続として別のCPSな関数を渡す、その別のCPSな関数に継続としてさらに別のCPSな関数を渡す、…の連鎖(CPSな関数のネスト)が、全体としてCPSなプログラム。
  • 継続モナドでは、CPSな関数をContにくるんで >>= で繋ぐことで↑を実現できるようにしてある。

Contの定義

newtype Cont r a = Cont { runCont :: ((a -> r) -> r) }

  • runContが、そのCPSな関数。
  • つまり、引数に継続の関数(a->r)を取って、最後にその継続を呼ぶ(から継続の返値rが、この関数の返値r)
  • 動かすときは、runCont自体を呼ぶ。

bind関数(>>=)

(Cont c) >>= f = Cont $ \k -> c (\a -> runCont (f a) k)

  • c >>= f は、cに継続fを渡すのが仕事であるCPSな関数(をContでくるんだもの)。つまり、引数に継続の関数kをとり、cしてfしてkするCPSな関数(をContでくるんだもの)。
  • 「runCont (f a) 」の部分: f aが、CPSな関数をContにくるんだのを返す。runContでそのCPSな関数を取り出される。
  • 「runCont (f a) k」の部分: ↑に継続の関数としてkが渡される。つまり、f aは仕事の最後にkを呼ぶ。「faしてk」
  • 「c (\a -> runCont (f a) k)」 の部分: cはCPSな関数。それに対して、継続の関数として「a->faしてk」が渡される。cの仕事の最後で「a->faしてk」が呼ばれる。
  • 「\k -> c (\a -> runCont (f a) k)」 の部分: kを継続の関数として引数にとり、「cが最後にfを引数aで呼び、fは最後にkを呼ぶ」という、CPSな関数。
  • 結局、cしてfしてkする*2CPSな関数が合成された。それをContでくるんである。
class (Monad m) => MonadCont m where 
    callCC :: ((a -> m b) -> m a) -> m a 
 
instance MonadCont (Cont r) where 
    callCC f = Cont $ \k -> runCont (f (\a -> Cont $ \_ -> k a)) k

Call with Current Continuation

callCC f = Cont $ \k -> runCont (f (\a -> Cont $ \_ -> k a)) k

  • callccといいつつ、環境をキャプチャしたりせず、単にCPS連鎖でそれ以降をスキップするだけ。*3
  • fの継続はkだが、f自身の中でもう一階層下に継続連鎖を持っている事が前提。下の階層の継続連鎖から好きなとき(fの引数を呼ぶ)にkにジャンプするために使う。
  • 「(\a -> Cont $ \_ -> k a)」の部分: aを引数に取り、継続の関数として渡された関数をシカトして、k aを返すニセのCPSな関数(をContでくるんだもの)を返す関数。ニセのCPSな関数に継続の(CPSな)関数を渡すと、渡された関数以降の継続連鎖(CPSな関数のネスト)はスキップされk aが値となる。
  • 「(f (\a -> Cont $ \_ -> k a))」の部分: fは「CC(callCCの時点の継続)」を引数に渡して貰いたい関数。fがこの↑の「CC」を引数aで呼び出すと、「以降の継続連鎖(CPSな関数のネスト)はスキップされk aが値となる」ニセCPSな関数をContでくるんだモノになる。
  • 「\k -> runCont (f (\a -> Cont $ \_ -> k a)) k」の部分: 継続の関数kを引数にとり、ニセのCPSな関数\_ -> k aをContで来るんだものを返す、CPSな関数。
  • 結局、(ニセのCPSな関数(をContでくるんだもの)を生む)CPSな関数が合成された。それをContでくるんである。


でも、よい子は継続モナドは使っちゃダメだそうで。なのに、合成モナドの説明が継続モナドを例に使ってるので、しかたなく理解を試みた次第。

*1:ただ、そのCPSな関数自体を、継続として他のCPSな関数に渡す使い方をするので、その意味で継続であるとは言える

*2:cもfもちゃんと継続の関数を最後に呼ぶ限り

*3:callCC時点での、CPS連鎖の「次」を「現在の継続」として擬似的にキャプチャとは言えるかも