2007年 11月 25日

デザイン (笑)

バズワード化してる。もとからかw

Ruby のブロック

Ruby のブロックは実のところ proc {} を引数に渡すのを、その proc が一つの場合に限って簡略化して書けるだけなのだけど (実際はちょっと違うっぽいけど)、これがかなりうまくいってるのがおもしろいなぁ。

  • return を書かなくていい
  • ブロック (というか評価されないメッセージ) を簡単に渡せる

ってのはすごく重要だなぁ。

class TrueClass
    def then
        @ret = yield
        self
    end

    def else
        self
    end
end

class FalseClass
    def then
        self
    end

    def else
        @ret = yield
        self
    end
end

(foo == bar).then {
    puts "hoge"
}.else {
    puts "fuga"
}

ふつうに綺麗に書ける。

Boolean.prototype.then = function (fun) {
	// this は Object になっているので true になってしまう。
       // もとの値をとりだすために valueOf()
	if (this.valueOf()) fun();
	return this;
};

// else は予約語なのでつかえない
Boolean.prototype.els = function (fun) {
	if (this.valueOf()) fun();
	return this;
};

("foo" == "bar").then(function () {
	print("hoge");
}).els(function () {
	print("fuga");
});

ぜんぜん綺麗じゃない。


ちなみに Io だと if([pred], [then], [else]) 以外にもうえのと同じことがデフォでできる。(Io では引数は基本的に評価されない。Message オブジェクトとしてメソッドにわたる。ただし仮引数を書いた場合は自動で評価される)

("foo" == "bar") then (
	"hoge" println
) else (
	"fuga" println
)

. ではなくスペースをメッセージ送信のくぎりにすることはこういうところで生きてきますね。一見したら構文にしか見えない。(if は then/else の評価されたほうの値をかえすけど、else は nil をかえす。then はもちろん self をかえしている)

ラムダ計算 / オブジェクト指向

ラムダ計算では true/false はもちろん関数で、こんなかんじらしい (true は x と y を引数にとり x を返す関数で、false は y を返す関数)

t :: a -> b -> a
t x y = x

f :: a -> b -> b
f x y = y

main :: IO ()
main = t (putStrLn "hoge") (putStrLn "fuga")

Haskell で書いたはいいけど、あんまわかりやすくないなw (引数は評価されるけど、副作用はモナドでラッピングされてる(?)から、かえされた IO モナドインスタンスが保持してる putStrLn "hoge" しか表示されないってことだとおもうけど、というか用語がすでにあやしい。Haskell こわい)

(define (t x y) x)
(define (f x y) y)

((t
  (lambda () (print "hoge"))
  (lambda () (print "fuga"))))

Scheme で書くとこんなかんじかな。Haskell より綺麗に書けないけど、なんかまちがってるかな……(引数は評価されてしまうので lambda でかこみ、t の返り値をさらに呼びだしてる)

まぁどっちにしろこれってどことなく OOP で書いた

true then (
	"hoge" println
) else (
	"fuga" println
)

に似ていて、

true if := method(
	call evalArgAt(0)
)

false if := method(
	call evalArgAt(1)
)

("foo" == "bar") if (
	"hoge" println
,
	"fuga" println
)

とかくともっと似てますね! と最近思ったのでした。(もしかしたらあたりまえなのかもしれないけど、じぶんとしては、やっと気付けたことなのでした)

似ているからなんなの? っていうのはまだうまく言語化できない。なんというか、「ああそっかラムダ計算もオブジェクト指向も純粋だと似てくるんだなぁ」みたいな、よくわからないけど

あー Ruby で書くともっとわかりやすいかなぁ……

# lambda
t = lambda {|x, y| x }
f = lambda {|x, y| y }

t[lambda {
	puts "hoge"
}, lambda {
	puts "fuga"
}][]

# OOP
class TrueClass
	def if(x, y)
		x[]
	end
end

class FlaseClass
	def if(x, y)
		y[]
	end
end

true.if(Proc.new {
	puts "hoge"
}, Proc.new {
	puts "fuga"
})

ラムダのほうは似せるために curry 化してない。curry 化するとラムダのほうはもっと綺麗にかける。

# http://subtech.g.hatena.ne.jp/cho45/20071119/1195420784
t = lambda {|x, y| x }.curry
f = lambda {|x, y| y }.curry

t[lambda {
	puts "hoge"
}][lambda {
	puts "fuga"
}][]

curry 化バージョンは Scheme と全く同じことをやってる。

というか Ruby は表現しやすいなぁ。Io や Scheme や Haskell は初見だと読めないし……Ruby にしとくと関数系の人もOOP系の人も読めていいかんじがする。

ほげほげ

OOP ← Io - Ruby - Scheme - Haskell → ラムダ

この4つの言語やるとなんかいろいろわかる気がしてきた。JavaScript は return かかないといけないからここには入れたくないな (OOP にしたって関数にしたって return かかないといけないのは害にしかならない)

ラムダきわめると Lazy-K になるっぽいけど、OOP (メッセージセンド) を極めるとどうなるんだろう。Io でいいのかな。たしかに全部メッセージセンドだけど、違和感ある。なんでだろう。

関数型とOOPと書き順

頭にうかんだ順に書けるぶん、自分は関数的に書くより OOP で書くほうが好きだ。

# http://subtech.g.hatena.ne.jp/cho45/20071121/1195642614
0.countup.imap {|x| x * x }.take_while {|x| x < 10}
import Text.Show

countup = iterate (\x -> x + 1)

main :: IO ()
main = putStrLn . show $
       takeWhile (< 10) $ map (\x -> x * x) $ countup 0

メソッドチェインだと前から順番によんでいけるけど、関数よびだしだと頭の中のスタックに前から順につんでポップしていかないといけない。

関数系の人はたぶん違う方法で考えているんだろうなぁ……

でも関数の部分適用とかはたのしい うえのコードの (< 10) とか、countup の定義とかすばらしくたのしい。

あーいやうーん。OOP と関数の比較っていうのとは違うかんじもする。

Leopard ほしい!

http://rubycocoa.sourceforge.net/RubyInject

これのせいで俄然 Leopard が欲しくなった。

ほしい言語

たぶんプロトタイプベースな Ruby がほしいのかもなぁ。

self #=> Global env. Object

hoge = "hoge"
self.hoge = "hoge"
Global.hoge = "hoge"
Global.Global #=> Global

Global.proto #=> Object
Global.fun   #=> fun {|&block| [native] }

Object.foo = fun {
    self #=> a env. object having receiver as proto 
}

Array.map = fun {|&f|
    ret = []
    self.each {|i|
        ret << f(i)
    }
    ret
}

String.each = fun {|&f|
    split("").each(&f)
}

String.map = Array.slot("map")
String["map"] = Array.slot("map")
String.set_slot("map", Array.slot("map"))
String["[]="]("map", Array.slot("map"))

Array.clone.set(1, 2, 3) == [1, 2, 3] == Array.with(1, 2, 3)

o = Object.clone.do {
    self #=> just the `o`
    hoge = "hoge"
    self.foo = "foo"
    fuga = fun {|x|
        "fuga"
    }
}
o.hoge #=> "hoge"
o.foo  #=> "foo"
o.fuga #=> "fuga"
o.slot("fuga") #=> fun {|x| "fuga" }

Array.sum = Array.slot("inject").partial(0) {|r,i| r + i}
[1, 2, 3].sum #=> 6

括弧の省略をしても呼びだしされるあたりがなんかうまくいかなそう。文法の矛盾があったりして、寝ておきたら「だめじゃんw」と思うんだろうなぁ。

Ruby を書いててときどき、 JS みたいに関数を自由にあつかいたいと思う。Ruby はクラスベースで、メソッドはかならずどこかのクラスに属し、UnboundMethod は bind しないかぎりよべず、bind の対象ももとのクラスと関係がないといけない。だから……だからなんだろう……


うまくまとまらない。

したいこと

  • 環境 (変数をセットする場) もオブジェクトにして書きかえしたい
    • スコープチェイン == 環境オブジェクトのプロトタイプチェイン
  • 関数/メソッドはとりだしてつけかえたりしたい。
  • 関数/メソッドを部分適用したい
  • メソッドよびだしの括弧は省略したい
  • return かきたくない
  • 関数を簡単に関数にわたしたい。
  • 正規表現リテラルはほしい。
  • ダックタイピングしたい
  • if とか case とかは構文としてあっていい (無理にメッセージセンドにしない)
  • 関数はメソッドと同じように呼びだしたい。

Greasemonkey + Firebug + GM_log

あーふつうに GM_log つかえてた……

Firebug 1.0 以降では extensions.firebug.showChromeMessages を true にしないと GM_log のメッセージが表示されないらしい。true にしたらちゃんとでた……

uneval

toSource のラッパっぽい uneval っていう関数があるみたいだ (いつからあるんだろ)。toSource は null とか undefined のときエラーになるけど (オブジェクトじゃないから) これだとならない。

uneval(null); //=> "null"
uneval({1:undefined}); //=> "({1:(void 0)})"

http://malblue.tumblr.com/post/20163446

かなりまえからあるらしい。(1.5) String.prototype.quote とかいうのもあるらしい。Ruby の String#dump 相当かなぁ

jAutoPagerize が Safari で動くようになったよ

贅沢言わなければ動くようになったよ。Safari の innerHTML 系メソッドは link, meta も消しちゃうので、それを nextLink に指定していなければ動くよ。ただ、なぜかユーザスクリプトが実行されないページがあるので要検証

http://userscripts.org/scripts/show/14666


「もういいや」とか書くとなんか続けたくなる謎

2007年 11月 24日

シンクロニシティ

捨てネコらしきネコが家の庭にきていすわって対処に困っているのだけれど、いろいろありつつ飼うことになりそうな流れになっている。とかいう感じで名前つけるならなんだろうなぁ、とか考えてマリーとかどうよとか思ってたんだけど、なぜか名前の話なんて全くしていないのに母親と案がまるかぶりした。

余談ですが、今メインでつかっている MacBook のマシン名が Mary です。母親はマリーゴールドからとったようです。

jautopagerize Safari (GreaseKit) 対応

かなり初期のころの挙動にもどした。innerHTML つかうのと変わらないなぁけっきょく。はてダみたいに head/link@href を見てるやつだと2ページ目以降とれない。ふつうのサイトは body 以下にも同じリンクがあるから、siteinfo のほうを変更することで対処できるけど……

あーなんかほとんどうごいてないなぁ。もういいや……Safari とか使わないし

Safari とは関係ないけどごちゃごちゃやってるうちに Fx + Google でちゃんと動くようになった……

XSLTProcessor

Fx で HTMLDocument つくるのに XSLTProcessor つかうようにしたんだけど、よくわからない条件でエラーがでる。

[Exception... "Component returned failure code: 0x80600001 [nsIXSLTProcessor.importStylesheet]" nsresult: "0x80600001 ()"

div + innerHTML + XPath 改変のほうが安定しそうだなぁ……なんでこのへんマトモな実装してないんだろ。意味不明すぎ (Safari3 と Opera には createHTMLDocument がある)

XSLTProcessor つかうのやめた。はてダででるんだよなぁ (はてグなら大丈夫)
id() についてはもともと Safari 向けのコードで //*[@id=''] に文字列置換するのを書いてる。二個以上 id 指定があるとだめだけどそんな使いかた普通しないから無視

Fx に createHTMLDocument がないのは createDocument に doctype わたせばいいってことらしいけど (というか DOM の仕様にないのはそういうことなんだろうけど)、

var html4dt = document.implementation.createDocumentType("html", "-//W3C//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict.dtd");
alert(document.implementation.createDocument("", "html", html4dt));

とかやっても普通に XMLDocument になるよ。イミフ。これってバグじゃないのかなぁ。でも Bugzilla には関わりたくない。

memo

  1. http://mxr.mozilla.org/firefox/source/content/base/src/nsDocument.cpp#706
  2. http://mxr.mozilla.org/firefox/source/content/base/src/nsContentUtils.cpp#3319
  3. http://mxr.mozilla.org/firefox/source/content/xml/document/src/nsXMLDocument.cpp#102

普通に XML の処理しか書いてない。

2004 年報告のやつだけどまだ NEW だ。NEW の意味わすれたけど

2007年 11月 23日

Fx3 対応の AutoPagerize

何度か AutoPagerize の書きかえに挑戦して挫折 (GP にうつってから AutoPagerize なしで生きてた) しているのでゼロから書きなおした。まだ途中だけど30%ルールぐらいでコミット (はてダ/はてグぐらいしか確認してないんだぜ……)

http://coderepos.org/share/browser/lang/javascript/userscripts/jautopagerize.user.js?

Mochikit の Deferred っぽいのをとおしてリソースのキャッシュとかを綺麗に書けるようにしてみた。コンパクトにしようとおもってたら API がちょっとキモくなってしまった。(メソッドチェインできることと、addCallback みたいに camelCase のどこを大文字にするのかわからないような名前をつけないことには気をつけた)

Fx3 の JS (JS1.8) には reduce があって嬉しいですね。あと 1.6-1.8 追加の関数は MDC に等価コードがのっているので、他のブラウザになくてもコピペすれば動くようになるので使いまくれる。

確認したかぎり最新の Greasemonkey (0.7.20071121.0) をつかってる。普通の GM だとまだ Fx3 にインストールできない。このバージョンでも locale が ja-JP だと動かないので en-US にしないとだめ。


セキュリティ関係で unsafeWindow.console.log とかできないっぽいのでデバッグがかなりめんどうくさい。深追いしてないからできるのかもしれないけど……あと GM_log も動いてないっぽい。


Safari (GreaseKit) とか Opera でも動くようにしたいのであんまり Fx 特化の技はつかいたくない (XSLT つかって HTMLDocument つくるとか……BK すぎる……)

console.log へ吐く方法はみつけた。

	function log (m) {
//		var c = unsafeWindow.console;
//		if (c) c.log.apply(c, arguments);
		var o = Array.prototype.concat.apply([], arguments);
		location.href = "javascript:(function () { if (window.console) console.log.apply(console.log, "+o.toSource()+") })();";
	}

カッコではじまる XPath はうまくいかない。根本的な解決はむずかしい。

なんで nextLink は属性値まで指定させる方法じゃないんだろ。form/@action とかのやつも siteinfo にそのまま書けるのに……

script 要素のタグは削除してるけど内容を削除してないのではてブ人気エントリとかでゴミが表示されてしまう。実害ないからとりあえず放置

AppleScript / RubyOSA++

require "rubygems"
require "rbosa"

safari = OSA.app("Safari")
safari.do_javascript("alert(location.href)", safari.documents[0])

jautopagerize の Safari 対応がなんかむりくさい感

toSource と reduce を実装して iframe で document とるようにするところまではうまくいくのだけど、そのあとなんか設定されているはずの変数が undefined になったりして意味不明すぎる (たまにうまくいく)。それにアクセスすると Safari がフリーズする。GC がおかしいのかなぁ。

なんかもっと別の方法考えないとだめなのかもしれない。(Safari は innerHTML すると html/body/link とか消してしまうので使えない)

ちなみに Fx3 で iframe つかって document とらないのはとれないからなんだけど、とる方法あるのかな。とれるならそのほうが簡単なんだけど