2007年 11月 06日

モナド・モナド・モナド

Haskell が理解できる気がしてこない。諦めたい気分になってくる (実際書いてるコードは理解しなくても書ける範囲でしか書いてない)。いやもちろん誰かに「やれ」なんていわれているわけじゃない。理解できなくてくやしい。lift が全くわからない。「モナドの全て」の説明を読んでもわかった気にさえなれない。頭が悪い。本当に悪い。頭悪いのを実感できる分野は苦手だ。数学とかまさに。Haskell は数学に近い。ただプログラミングということになっているから耐えられる。いややめるかも。頭のいいわるいなんて生まれつきだから、そんなのいまさらどうしよもない。理解できる人には理解できるんだろうなぁ。

IO モナドが副作用を閉じこめているのは (そういう説明がされているから) わかった気になる。それを扱うとき、そのモナドの中で (この表現が正しいかよくわからない。つまり do ブロックの中といいたい) 処理をして、モナドをはずす (この表現が正しいかよくわからない。つまり IO String を <- をつかってなかの String をとりだすといいたい) ことで、副作用を一旦どっかに置いとくことができるようなことはわかった (ほんとに?)。

この do ブロックの中でなにがおこっているのかわかってない。>> で各行が連結されている? >> はなにをする? 連結。連結ってなに。>>= の = がないバージョン。return がなんなのかわかってない。こいつはなにをするんだ。

return は return :: a -> m a すなわち a をとってモナドにくるまれた a をかえす。モナドのインスタンスをつくる。でもこれ引数に値しかわたしてないけど、どのモナドにくるまれるんだ? コンテキストモナド? コンテキストモナドとかてきとうにいったけど、よくわかってない。それが存在するかすらわかってない。

(>>=) :: m a -> (a -> m b) -> m b バインドとかいうらしい。モナドインスタンス m a と関数を一個とって新しいモナドをかえす。わけわかんない。モナドの中の値を処理してまた新しくモナドインスタンスをつくってる。なんでこんなことする必要があるんだろう。計算を合成するってどういう意味だ。Maybe があるなし一緒にして計算を継続できるようになってるのはいいことなのか。合成ってなんなんだ。関数合成と計算の合成って何がちがうんだ。

そういえば関数合成 . と $ のつかいわけもよくわからない。全部 $ でかけるような。合成のほうが気分的にすっきりするだけ?

aaa >>
bbb

aaa >>=
(\x -> bbb)

みたいなときに x には何が入ってるんだ。aaa の返り値か。まてまてモナドはどこいった。もうすこし実用に近いところでみてみよう

import Network.CGI

cgiMain :: CGI CGIResult
cgiMain =
    setHeader "Content-Type" "text/plain" >>=
    \x -> output "Hello"

main :: IO ()
main =  runCGI $ handleErrors cgiMain

もちろん >>= は >> にして \x -> は消しても動く。それはいい。\x には何が入っているんだ。setHeader の返り値のはずだ。

setHeader :: MonadCGI m => String -> String -> m ()

m () ってなんだ。() がなんだかわからない。null ? 空リスト? いやリストは [] だ。こいつはなんだ。Haskell だと print デバッグができない。\x になにが入っているか直接調べることができない。学習のさまたげだ。print デバッグは偉大だ。

この () とかいうやつは :type しても () そのままうちこんでも () のままだ。どこに定義が書いてあるんだ。探しにくい。これだから記号がいやだ。

まぁいいやもっと簡単にしていこう。こんどはちゃんと前の計算の値をつかうモナドだ。よくあるやつになった

main :: IO ()
main =  readFile "Rakefile" >>= (\x -> putStr x)

readFile は IO String をかえす。>>= で x に IO String のなかの String がバインドされる。そして計算が実行される。

実際は putStr 自体が (\x -> fun x) の形になっているからこう書く必要はなく

main :: IO ()
main = readFile "Rakefile" >>= putStr

でいい。putStr をよばずに、関数として >>= 関数にわたしてやっているだけだ。ん? なんか全く普通だ。関数の定義通りの実行されているにすぎない。モナドが見えない。putStr の返り値は IO () だから main の定義ともあっている。これはまったくもってわからないところがない。あれ?

main :: IO ()
main = readFile "Rakefile" >> putStr ""

たしかになにもでない。あたりまえだ。

もう一回 CGI の例を定義とてらしあわせてみるけど、こっちはやっぱりわからない?

class Monad m => MonadCGI m
data CGIT m a
type CGI a = CGIT IO a
runCGI :: MonadIO m => CGIT m CGIResult -> m ()
setHeader :: MonadCGI m => String -> String -> m ()
output :: MonadCGI m => String -> m CGIResult

--

import Network.CGI

cgiMain :: CGI CGIResult
cgiMain =
    setHeader "Content-Type" "text/plain" >>
    output "Hello"

main :: IO ()
main =  runCGI cgiMain

最終的には IO () を runCGI が返すはずだ。だけど runCGI は m () をかえしている。ここでの m は MonadIO だ IO は MonadIO のインスタンス? だとするとここはあってる。runCGI は CGIT m CGIResult を引数にとる。cgiMain は CGI CGIResult をかえしてる。あれ m は? とおもったら CGIT m か。CGI CGIResult は type を展開すると CGIT IO CGIResult になる。うむ CGIT m CGIResult とマッチする。

setHeader は String -> String をとって MonadCGI をかえしてる。すなわちここでは setHeader が MonadCGI の最初のインスタンスをつくっている (たぶんこのインスタンスが setHeader した結果を保持しているんだろう)。そして、output は m CGIResult をかえしている。m は MonadCGI のインスタンスだ。あれ m CGIResult と CGI CGIResult があってなくないか。data 構築子 CGIT は m a と定義されている。m は MonadCGI。ということは m CGIResult になるな。なるほど。

import Network.CGI

-- 1: cgiMain :: CGI CGIResult
-- 2: cgiMain :: CGIT IO CGIResult
-- 3: cgiMain :: MonadCGI m => m CGIResult
cgiMain :: MonadCGI m => m CGIResult
cgiMain =
    setHeader "Content-Type" "text/plain" >>
    output "Hello"

main :: IO ()
main =  runCGI cgiMain

これでもうごく。ちゃんと整合性とれてる。data CGIT m a のよみかたがわかった。data CGIT は m a と一緒ってことだな。たぶん。

とりあえず型のうごきをみていくと >> のところでモナドインスタンスがうけわたされて処理されているっぽいのがなんとなくわかった。前回の計算結果は捨てているけど、背後でうごめくモナドインスタンスは引き継がれている。ベルトコンベアの比喩も若干わかった。文章にするのはわからないところがわかってそれを理解しようとする試みができて偉大だな。

しかしモナドインスタンスがどうやって計算を保持しているかわからないな。モナドによって保持方法が違うのは想像できるけど、実装はどうなっているんだろう。

まぁそれはおいといて、return もすこし理解できた。上ではでてきていないけど、>>= の右側の関数は m a をかえさないといけないわけだ。でも内部処理した結果がただの String だったりしたときは、モナドでつつみなおさないといけない。そのときん return をつかうとそれをつつんで、次にわたせるってことかな? よしやってみよう。

import Network.CGI

cgiMain :: CGI CGIResult
cgiMain =
cgiMain =
    setHeader "Content-Type" "text/plain" >>
    return "hogehoge" >>=
    output

main :: IO ()
main =  runCGI cgiMain

こうすれば return でつつまれて次の関数に連結され、output に hogehoge がわたされるはずだ。うまくいった。うれしい。


とりあえずつかれた。ここまでやるのに時間かけすぎだ。リストモナドがなんでモナドなのか理解していない。lift は理解できそうもない。Haskell は if も case も let も全く理解していない。まだまだだ……

関数合成 .

test :: String
test = "aaa" >>=
      return . toUpper

みたいにかけるのはいいかも

test :: String
test = "aaa" >>=
      \x -> return $ toUpper x

はちょっとださい。


リストもモナドっていうのをたしかめたくて変なコード書いてる。String は Char のリストなのでモナドの演算ができるはず (直接やらないのは String のほうが出力しやすいから) うえのは単に map (\x -> toUpper x) "aaa" と一緒だから良さがよくわからないけど、リストモナドから要素を一個一個とりだして関数適用してるっぽいのはわかったかも。

2007年 11月 04日

IMG_61210079

IMG_61350080

libruby.1.dylib うめこむやつ

http://rubycocoa.sourceforge.net/EmbedRubyCocoa

require 'pathname'
exec = File.join(ENV['TARGET_BUILD_DIR'], ENV['EXECUTABLE_PATH'])
old = Pathname.new('/usr/lib/libruby.1.dylib').realpath
new = '@executable_path/../Resources/libruby.1.dylib'
system("install_name_tool -change '#{old}' '#{new}' '#{exec}'") or exit 1

ってうまくいかないようなきがする。Contents/MacOS/APPNAME は otool -L すると libruby にリンクしてなくて、@executable_path/../Frameworks/RubyCocoa.framework/Versions/A/RubyCocoa にリンクしてるし、実際 libruby にリンクしてるのはこっちのほう。install_name_tool の挙動を勘違いしていなければ、たんにこれは指定されたファイルの Mach-O dylib のパスを書きかえるだけだろうから、これだと意図したこと (バンドル内の libruby よむ) ようにならない?

Chemr とかでは結局 standalonefy してあつめられる .bundle とかの libruby.1.dylib へのリンクも一緒に書きかえるようにしてみた。

# in 'package' task
	# copy libruby.1.dylib
	sh %{rubycocoa standaloneify "build/#{DEFAULT_CONFIGURATION}/#{APPNAME}.app" "image/#{APPNAME}.app"}
	system_libruby = Pathname.new("/usr/lib/libruby.1.dylib").realpath
	bundle_libruby = "image/#{APPNAME}.app/Contents/Resources/libruby.1.dylib"
	linked_binary  = "image/#{APPNAME}.app/Contents/Frameworks/RubyCocoa.framework/Versions/A/RubyCocoa"
	(Pathname.glob("image/**/*.{bundle,dylib}") + [linked_binary]).each do |l|
		sh %{install_name_tool -change '#{system_libruby}' '@executable_path/../Resources/libruby.1.dylib' '#{l}'}
	end
	cp system_libruby, bundle_libruby
sudo mv /usr/lib/libruby.1.dylib /usr/lib/libruby.1.dylib_  

して起動することを確認してみた。

(たぶん) 完全にスタンドアロンな Chemr と Amalgam

起動バイナリを置きかえて DYLD_LIBRARY_PATH を設定するとかでもいいのかなぁ。この環境変数がどれぐらい強いのかよくわからないけど

マルチログイン系のを GM で書かない理由

使わないときにも実行されるのが嫌

GM で書いたほうがはやく書けるけど、日常的におもくなってストレスたまるほうがいやだな。


GranParadiso はそれでなくても重いし!!!!! というか Safari の UI を RubyCocoa で InputManager とかつかって変更できるなら乗りかえちゃうなとか思った。

>

GranParadiso

CPU つかいすぎだなぁ……あとなんかフォアグラウンドにいるときはより CPU を食う。ActivityMonitor のグラフがやたらのびてるなぁとおもってそっちにスイッチすると隠れようとする。

関係ないけど pmTool も CPU 食いまくるよなぁ。そのせいでバッテリ持たないっていうのはどうなの。

2007年 11月 03日

そういえば

id:gyuque さんにカロリーメイトのパチモンをもらったのを忘れていた。というか写真も現像してない。どんだけ coderepos 中心の生活なんだ。へたなネトゲよりたのしいコミット。

IMG_61760084

id:higepon さんと mayuki さんにもわたったようだ。

あと、昨日の写真でアップロードできそうなのを探したけどみごとに人がうつっててやめた。肖像権こわい。

IMG_61500085

前半一番後ろに座ってたときのやつがあった。(後半は空気読まず一番前に座ってた)

Shibuya.js

いい空気だよなぁほんと……いごごちがいい。今回は懇親会で Perl hacker なかたがたの Perl よりの話を聴いた。じわじわ Perl の知識が増えておもしろい。

あと、学校辞めてから生き生きしてるよねとか言われたけれど、まじでその通りすぎる。やらなくちゃいけないことはあるんだけど、そういうのさしひいても毎日楽しいもんなぁ。

写真の統一感

は、彩度とかコントラストとかではなくて、なんかちょっとそういう要素とは違う部分にあると思うなぁ。フィルタのかかりぐあい。心の。フィルタの状態の統一感ではないかな

機械的に判断できる部分ではなくて、頭に思い浮かべた像が、統一されているかどうか。

GranParadiso

Firebug1.1.0b7 を入れた。うごいている。しかしいままで以上に重い。GranParadiso の重さと相乗効果

はてなのマルチログイン uc.js がないと激しく不便なのでデバッグして Fx3 向けに修正した。tumblr のやつも同じ修正が必要だけど、あんま使ってないのでそのうちでいいや。

http://coderepos.org/share/changeset/1051

ekfloras (blosxom clone) のプラグイン機構

とりあえず module/pluggable を使っていたものの、別にリロードとかできなくていい (CGI なので使い捨て) ので、もっと簡単なプラグインシステムにした。ついでに rubygems への依存もなくなったので、純粋に Ruby がインストールされていればレポジトリからチェックアウトしてすぐ動くようになった。

# プラグインファイルをロードして
# インスタンスの配列を実行すべき順にソートして返す
def load_plugins
	Pathname.glob(@opts[:plugin_dir] + "/**/*.rb") do |f|
		load f
	end
	pm = self.class.const_get(:Plugin)
	pm.constants.inject([]) {|r,i|
		r << pm.const_get(i).new(self)
	}.sort_by {|i|
		i.priority
	}
end

みたいにして Ekfloras::Plugin::* をプラグインとみなし、メソッドをよぶ。かなり簡単だけど普通は十分なプラグイン機構だと思う。(ファイル名とクラス名間の名前の制約がなくなるので、てきとーにプラグインんが書けるようになる。逆にこれのせいでリロードとかが実装できない) こんだけ単純だと特にライブラリする必要もないので欲しくなったときに書きなぐればいいのがいい。

(priority メソッドの返り値でソートしているのは、プラグイン同士で実行順に気をつかうようにするため。blosxom だと名前順になっているから、各ユーザがプラグインの仕組みを理解してファイル名を書きかえたりしないといけなかったけど、これでだいたいてきとーにプラグインディレクトリにほうりこんでも動くようになるはず)

ちゃんとインスタンス化してメソッドをよぶようになるので、各プラグイン内のメソッドの名前がコンフリクトしたり、データが共有されることがなくていい。ちゃんとメソッド間ではデータ共有できるし (こんなのあたりまえなんだけど、Ruby 系のプラグイン機構ってこういうあたりまえのことができないのが多いイメージ)

あとは設定かな。設定はできるだけ書きたくないから (blosxom のプラグインのいいところは投げこめば動くことだと思う)、最小設定と、オプションをいくつかとかになるかな。

ekfloras の速度

  1. #<Ekfloras::Plugin::Meta:0xb7bcc03c>
  2. #<Ekfloras::Plugin::Paging:0xb7bcbfec>
  3. #<Ekfloras::Plugin::Comment:0xb7bcc014>

(EntriesCache なし)

ruby 1.8.4
1159 files, 1.212316 sec
1159 files, 1.722761 sec
1159 files, 1.390812 sec
1159 files, 1.353448 sec
1159 files, 1.423345 sec

ruby 1.9.0 (2007-09-24 patchlevel 0) [i686-linux]
1159 files, 1.025227 sec
1159 files, 1.075664 sec
1159 files, 1.202309 sec
1159 files, 1.300395 sec
1159 files, 1.284752 sec
  1. #<Ekfloras::Plugin::EntriesCache:0xb7c08ae8>
ruby 1.8.4
1159 files, 0.733907 sec
1159 files, 1.166876 sec
1159 files, 1.437776 sec
1159 files, 0.857321 sec
1159 files, 0.906379 sec

ruby 1.9.0 (2007-09-24 patchlevel 0) [i686-linux]
1159 files, 0.315943 sec
1159 files, 0.301194 sec
1159 files, 0.446674 sec
1159 files, 0.254224 sec
1159 files, 0.45789 sec

EntriesCache (エントリをオブジェクト化した状態でマーシャライズしてキャッシュするプラグイン) 入れた ruby1.9 がかなりはやい。