2012-01-10
■ [scheme] R6RS Enumerationsに関する調査
というメモが見つかったので貼っておく。
Recordsほどではないがなんか無駄にややこしいような…
参考
(define-struct enum-type (universe members indexer constructor)) (define-struct enum-set (type members))
型
各enum-setはmembers(シンボルのリスト)とtype(enum-type)をもつ。
各enum-typeはuniverse(母集合となるシンボルのリスト)、members(??)、indexer(シンボルから番号を返す手続き)、constructor(シンボルのリストからenum-setを作る手続き)をもつ。
よって、各enum-setは自分の母集合であるシンボルのリストを(enum-type経由で、間接的に)もつ。これを元文書ではenum-setの「universe」と呼んでいる。
enum-setはfirst classのデータ構造である。例えばypsilonでは「#<enum-set (a b c d)>」のように表示される(ypsilonの場合、属するtypeは表示されない)。Larcenyだと単に「#<record enum-set>」のように 表示される。
しかしやっかいなことに、enum-typeはfirst classのデータ構造*ではない*。R6RS Enumerationsでは、各enum-setに対しそのenum-typeを取得するようなAPIは用意されておらず、 Schemeの世界に取り出すことはできない。あるenum-setの母集合を知りたいときは、(enum-set-universe s) として、「sと同じenum-typeに属する、母集合の全ての要素を含んだenum-set」を得るという仕様になっている。また2つのenum-setが同じenum-typeに属しているかどうかを知る方法もない*1。
さてそうすると、「どうやってあるenum-typeのenum-setを作るのか?」という疑問がわく。答えは以下。 define-enumerationでenum-typeを定義した場合は、第三引数で指定した名前がenum-setのコンストラクタ(enum-setを生成する手続き)になる。
(define-enumeration color (red green black white) color-set) (color-set red black)
もう一つ低レイヤのAPIがあって、(enum-set-constructor s)とすることで、「sと同じenum-typeのコンストラクタ(enum-setを生成する手続き)」を取得することができる。 また(make-enumeration '(a b c))で、universeが(a b c)であるような新しいenum-typeを定義することができる。ただし返り値はenum-typeではなく(前述のようにenum-typeはfirst classではない)、#<enum-set (a b c)>が返る。
制限
以下の操作では、2つのenum-setは同じenum-typeに属していなければならない。
- (enum-set-union es1 es2) -> enum-set
- (enum-set-intersection es1 es2) -> enum-set
- (enum-set-difference es1 es2) -> enum-set
以下の操作では、2つのenum-setは別のenum-typeであってもよい。
- (enum-set=? es1 es2) -> bool
- シンボルの集合として等しいかを返す。
- 両者が別のenum-typeであっても、シンボルの集合として等しければ#tが返る。
- (enum-set-subset? es1 es2) -> bool
- es1がes2のサブセットであるとき#tを返す。
- 両者が別のenum-typeであるときは、universeについても判定が行われる(即ち、es1のuniverseがes2のuniverseのサブセットでなければ#tが返らない)。
- (enum-set-projection es1 es2) -> enum-set
- これはむしろes1にes2が異なるタイプを指定するものかな。
マクロ
R6RS Enumerationsはマクロ(構文)を一つ定義する。define-enumerationがそれだ。
ypsilonでは、(define-enumeration type-name (symbol1 ...) constructor-syntax) をsyntax-rulesでdefineとdefine-syntaxに展開し、 展開されたdefine-syntaxではsyntax-caseを使うというちょっとテクニカルな実装になっている*2。
まずdefineでconstructorという一時変数にコンストラクタを束縛する。
(begin
(define constructor (enum-set-constructor (make-enumeration '(symbol1 ...))))
次に(color red)のような構文を定義する。これが「マクロ展開時に引数がuniverseに含まれているかのチェックを行うこと」という定義があるので、syntax-rulesではなくsyntax-caseを使わないと書けないんだよね。symbol2はこの例でいう'red。(symbol1 ...)はenum_type定義時のuniverse。(_ symbol2) はパターンマッチで、(type-name symbol2)と書いてもいいんだけど、このsyntax-caseが使われる時点で 最初の要素はtype-nameにマッチするに決まっているから、ワイルドカード「_」を指定している。syntax-case/rulesではおなじみのイディオム。
(color red)は何をするかというと、単に'redを返す。それだけ。 ただし引数がuniverseに入ってるかはチェックするので、(color asdf)とかはエラーになる。 (正直使いどころがよく分からない…(color purpel)みたいにtypoしたときにエラーになるから 安全ですよ、ということかな)
(define-syntax type-name
(lambda (x)
(syntax-case x ()
((_ symbol2)
(or (memq (syntax->datum (syntax symbol2)) '(symbol1 ...))
(syntax-violation 'type-name "excpectd symbols which belong to the universe" x))
(syntax 'symbol2)))))
最後に(color-set red black)のような構文を定義する。
(define-syntax constructor-syntax
(lambda (x)
(syntax-case x ()
((_ symbol3 (... ...))
(or (for-all (lambda (e) (memq e '(symbol1 ...)))
(syntax->datum (syntax (symbol3 (... ...)))))
(syntax-violation 'constructor-syntax "excpectd symbols which belong to the universe" x))
(syntax (constructor '(symbol3 (... ...))))))))))))
■ [scheme] Mac上にいろいろなR6RSインタプリタをインストールする (2012/01版)
R6RSのサイトに実装へのリンクがある。
Homebrewで入れられるもの
(MacPortsにもいろいろパッケージがあると思いますが、使ってないので省略)
Ypsilon
$ brew install ypsilon $ ypsilon foo.scm
mosh
$ brew install mosh $ mosh foo.scm
パッケージがあるもの
Chez Scheme
商用だが、フリー版の「Petite Chez Scheme」がある。
http://www.scheme.com/download/ から「Petite Chez Scheme」「32bit or 64bit」「threaded or nonthreaded」 「Petite Chez Scheme for Intel/AMD MacOS X」「*.pkg(インストーラ) or *.tar.gz(ソース)」を選ぶ。
今回はpcsv8.4-i3osx-1.pkg.tar.gzを選んだ。インストーラに従うと/usr/bin/petiteがインストールされる。
PLT Racket
brewでも入るけど、インストーラでバイナリを入れる方が簡単だったような。
起動
$ /Applications/Racket\ v5.1.1/bin/racket -f foo.scm
Larceny
http://www.larcenists.org/download.html から「prebuilt for Macintosh OS X/IA32」をダウンロード。
起動
LARCENY_ROOT=/Users/yhara/research/Scheme/larceny-0.97-bin-native-ia32-macosx larceny -- foo.scm
その他
Ikarus
開発が止まっていて、Vicareに引き継がれてるらしい。Vicareも6月からコミットがないが…
Guile
$ brew install guile
で入るんだけど、使い方がよく分からない…(そもそもどの程度R6RS準拠なのか)
$ echo '(import (rnrs (6)))' | guile guile> Backtrace: In standard input: 1: 0* (import (rnrs (6))) standard input:1:1: In expression (import (rnrs #)): standard input:1:1: Unbound variable: import ABORT: (unbound-variable) guile>
IronScheme
未トライ http://ironscheme.codeplex.com/
BiwaScheme (おまけ)
$ brew install node $ brew install npm Error: No available formula for npm npm can be installed thusly by following the instructions at http://npmjs.org/ To do it in one line, use this command: curl http://npmjs.org/install.sh | sh $ curl http://npmjs.org/install.sh | sh $ npm install biwascheme
2012-01-08
■ [ruby] myrurema 0.3.0をリリースしました
最新のbitclustで動かないというレポートをいただいたので(ありがとうございます)直しました。
あとrefeでは「refe Ar i」でArray#indexなどが検索できるというのを知った(思い出した?)ので、それにも対応しました。
/Users/yhara $ rurema Ar ind (1)Array#index (2)Array#indexes (3)Array#indices which one? >
■ [ruby] gem生成ツールをJewelerからBundlerに乗り換えた
BundlerはRubyGemsの依存関係をメンテナンスするツールだけど、実はJeweler等のようにgemを生成するための機能もある。
bundle gemコマンドでひな形を生成することができ、あとはrake buildでpkg/foo-x.y.z.gemができたりとかそういう感じ。
/tmp $ bundle gem foo
create foo/Gemfile
create foo/Rakefile
create foo/.gitignore
create foo/foo.gemspec
create foo/lib/foo.rb
create foo/lib/foo/version.rb
Initializating git repo in /private/tmp/foo
既にあるプロジェクトをbundlerに移行したい場合も、まず上のように/tmpにひな形を作ってから、それを参考にGemfile, Rakefile, lib/*/version.rbを書くのが良いと思う。
Jewelerとの違いは以下。
- 生成される*.gemspecがシンプル。
- バージョン名をVERSIONというファイルに書くのではなく、lib/foo/version.rbのFoo::VERSIONという定数から取得する。
- Gemfileを書く必要がある。が、最近のBundlerはGemfileに「gemspec」と書くと*.gemspecのdependencyを見てくれる機能があるので特に手間ではない(例)
(Jewelerもだけど)git前提なのでそこは注意。
■ [scheme] beginで囲まれたdefineに展開されるマクロを書いたときの、定義される変数のスコープについて
BiwaSchemeにInternal defineを実装するために、R6RSの仕様を読んでいる。そこで一点疑問が。
11.4.7 Sequencingによると、 (begin (define ..) (define ..)) みたいなbeginは「spliced into the surrounding body, as if the begin wrapper were not actually present.」のように書いてある。
つまりこういうことができる。
(begin (define a 'a) (define b 'b)) (display a)
ならば、以下のようなこともできるのかと思ったのだけど、そうとは保証されてないみたいだ。
(define-syntax m
(syntax-rules ()
((_)
(begin
(define a 'a)
(define b 'b)
))))
(m)
(display a)
てか、beginで囲んだdefineがspliceされるのってこういうことするためだと思ってたんだが、違うのかな。
できなかったやつ
/Users/yhara/proj/biwascheme % /Applications/Racket\ v5.1.1/bin/racket -f _misc/definition.scm reference to undefined identifier: b /Users/yhara/proj/biwascheme % petite _misc/definition.scm Petite Chez Scheme Version 8.4 Copyright (c) 1985-2011 Cadence Research Systems Exception: variable b is not bound /Users/yhara/proj/biwascheme % ypsilon _misc/definition.scm error: unbound variable b ...
できたやつ
/Users/yhara/proj/biwascheme % LARCENY_ROOT=/Users/yhara/research/Scheme/larceny-0.97-bin-native-ia32-macosx larceny -- _misc/definition.scm Larceny v0.97 "Funny in the Head" (Aug 19 2009 04:24:46, precise:Posix:unified) b /Users/yhara/proj/biwascheme % gosh _misc/definition.scm b
2012-01-05
■ [ruby] 簡単にRuby/Rails界隈の「事情通」になる方法
ブログを読んでいる人から、どこでRuby関係の情報を仕入れているのかと聞かれることが(希に)ありますが、最近の大きな情報源はこれです。
RubyInsideの中の人でもあるPeter Cooper氏のやっているメーリングリストで、登録するとこんな感じのメールが週一で届きます。 あとはタイトルをざっと見て、面白そうなのがあればクリックすると。
日本人で読んでる人まだそんなに多くなさそうなので、ここを情報源に日本語の解説記事なんか書くとけっこう喜ばれるんじゃないかと思います。
ちなみに同じ運営者のJavaScriptWeeklyというのもあって、こっちも面白いです。
(1/13追記:トップページを見たら、HTML5 Weeklyというのもやっているそうです。あと、今日Status Codeという新メルマガのお知らせが来ました。「プログラミング言語によらない話題」ということなので、どんな内容になるのか今から楽しみです。)
2011-12-31
■ [misc] 2011年を振り返る
大学3回生のときに始めたらしい本ブログも、特に何も考えずに書いているうちに継続10年が見えてきた。昨年はどんなまとめだったのかなぁと思って見てみたら、 去年は何も書いてないらしい。WEB+DBの原稿で忙しかったんだろうか…。
1月
- [dev] 実はこの頃ruby-dev translationを作っていた。(原稿が忙しくなってしばらく休止)
- [game] 今は無きテトリスオンラインをちょっとやっていた。
2月
- [book] WEB+DB #5 (Rake, Capistrano, Chef) の原稿書き。
- Ruby技術者認定試験Gold合格。難しかったなぁ。
- [life] この頃から、会社の何人かでスタジオに行って楽器を鳴らして遊ぶイベントが発足。主にドラムを担当しています。
- [life] この頃、ぎっくり腰を患う。他にもいろいろあって*1、このシーズンはほとんどスノボに行けず。
3月
- [life] 前よりちょっとだけ良いアパートに引っ越した。
- 地震。
- [event] RubyKaigiに応募。これ以降「Rubyマスターになる方法」についてずっと考える。
- [game] DSのぷよぷよ7を買って、ついに自分でもプレイし始める (ニコ生観戦は昨冬から)。
4月
5月
- [mac] MacBookをSnow Leopardに上げた。homebrewに移行。
- [dev] ruby-dev translation 公開。
- [life] 整体に通い出す。整体はいろいろ面白かったのでまたどこかに書くかも(書かないかも)。
6月
- [book] 奇妙な言語本が絶版になっていることに気づく。
- [dev] ICFPC(lambda the gathering)に会社メンバーで出場。
- [event] 松江Ruby会議03。Enumerable#lazyの話をした。
7月
- [dev] BiwaSchemeがNode.js上で動くようになる。kenprattさんによってprototype.jsからjQueryへのマイグレーションが行われる。
- [event] RubyKaigi2011で発表。
- ケータイをINFOBAR A01に機種変。Android端末デビュー。
8月
- [event] 新潟大学でEsolangについて講演する。
- [dev] BiwaSchemeのgithubリポジトリをbiwascheme/biwaschemeに移動。
- [dev] るびまにruby-dev translationの記事を寄稿。
- [game] Wiiを購入してVCAぷよ通勢に。
9月・10月
- [blog] 燃え尽きてブログ投稿数がめっきり減る(笑)
- 本をいろいろ読んでいたらしい
- [dev] BiwaSchemeの作業をいろいろと。
11月
12月
- [dev] Rubyアソシエーションのgrantに採択される(2012年につづく)
2012年の目標
- 健康を維持する
- 英語でもっとコミュニケーションできるようになる
*1 山陰の記録的大雪、先輩の第一子誕生、など
■ [misc] 2010年を振り返る
ついでに2010年も。この年は原稿を書いてたので発表はほぼ無し。移動が多かったらしい。 年末にyarvinstructionを公開したことで、「自分もちゃんとサイトを作れるんだ」という自信になった(Railsアプリを公開したのは初めて)。
- 1月
- 「Ruby基礎文法最速マスター」で初の1000ブクマを記録。この頃からブクマ数に拘らなくなる
- 2月
- Rails 3.0 betaがリリースされる。Rails勉強会@東京に参加。
- 3月
- はじめて確定申告をする。
- 4月
- gihyo.jpにModanShogiの記事を書く。出張でニューヨークに。初代iPadを買う。
- 5月
- WEB+DB連載第1回(Sinatra)の執筆。
- 6月
- ふと思いついてmyruremaを作る。
- 7月
- WEB+DB連載第2回(Ruby 1.9)の執筆。
- 8月
- RubyKaigi2010。Rubyゲーム会議で少しだけ喋る。
- 9月
- WEB+DB連載第3回(Heroku)の執筆。KMC面子でドイツ・ハンガリー旅行。
- 10月
- unite.vimを使い始める。さんたまジャグリングフェスティバルを見に行く。
- 11月
- WEB+DB連載第4回(jpmobile)の執筆。この頃、出張で鳥取とか岡山に行ってた。
- 12月
- yarvinstructionsというサイトをリリース。
2011-12-26
■ [rails] Rails3.1を使ってみた、あとRails 3.2とRails 4について
TL;DR: Rails3.1ではjs/cssが一級市民になったよ、あともうRails4の予定まであるらしいけどRails3ほどのbig changeではないらしいんで安心したよ
BiwaScheme BlackboardをRails 3.1で実装したので、社内勉強会でその話をしました。
情報源
- RailsGuides ( http://guides.rubyonrails.org/ )
- Rails3のことはここ読めばだいたい分かる
- Riding Rails ( http://weblog.rubyonrails.org/ )
- Rails公式ニュース
Rails 3.0 (おさらい)
Big rewrite
Rails2とMerbが合体してRails3になった
- 設計が見直され、よりすっきりした実装になった
- ルーティングの書き方がシンプルになった
- Action Mailerの使い方が変わった
- ARel
- scaffold等の生成するJSがunobtrusiveに
- Bundler導入
Rails 3.1
2011/8/31 Rails 3.1.0リリース
RailsGuidesにリリースノートがある。変更点がまとまっている
一番大きな変更は「Assets Pipeline」、アプリケーションの修正が必要 (config.assets.enabled = false でオフにしてしまうという手もあるけど、せっかくなら使ってみた方が)
あとは便利機能とか
- Reversible migration
- マイグレーションの「up」と「down」を別々に書かなくても良くなった
class MyMigration < ActiveRecord::Migration
# changeメソッドに必要事項を書くだけで、up/downをよろしくやってくれる
def change
create_table(:horses) do
t.column :content, :text
t.column :remind_at, :datetime
end
end
end
- HTTP Streaming
- レスポンスの生成が終わる前に、JSやCSSをダウンロードさせることができる
- Ruby 1.9.2が必要
- サーバが対応していることが必要(nginx+unicornとか)
- jQueryがデフォルトになった
- Identity Map
- 同一レコードに対するARのインスタンスが常に同じRubyオブジェクトになる機能
- デフォルトではオフ
Assets Pipelineとは
Rails 3.0以前:JS/CSSはpublic/javascripts, public/stylesheets以下にあった
しかし、最近のアプリだとRubyコードよりJSコードの方が多いケースまである
Rails 3.1以降:JS/CSSはapp/assets/javascripts, app/assets/stylesheets以下に
(JS/CSSが「アプリケーションの構成要素」として一級市民に)
/Users/yhara/proj/blackboard % tree app/assets/
app/assets/
├── images
│ └── rails.png
├── javascripts
│ ├── application.js
│ ├── bootstrap.js.coffee
│ ├── codes.js.coffee
│ ├── top.js.coffee
│ └── users.js.coffee
└── stylesheets
├── application.css
├── bootstrap_tweak.css.sass
├── codes.css.sass
├── scaffolds.css.scss
└── shared.css.sass
*.coffee --(変換)--> *.js --(結合)-(圧縮)--> /assets/application-0d36b244d76a0fa1eb2fef44636ce321.js *.scss --(変換)-->*.css --(結合)-(圧縮)--> /assets/application-39b67782315f18c881afc45b0fd7a8c6.css
(developmentでは結合・圧縮は行われない)
機能の詳細はRuby on Rails Guides: Asset Pipelineを読もう
なにができるようになった?
JS/CSSが1ファイルにコンパイルされるようになった
- 例:
<link href="/assets/application-39b67782315f18c881afc45b0fd7a8c6.css" media="screen" rel="stylesheet" type="text/css"> <script src="/assets/application-0d36b244d76a0fa1eb2fef44636ce321.js" type="text/javascript"></script>
- 読み込みが速くなる
- URLにハッシュ値が入るので、JS/CSSを変更したときにブラウザのキャッシュを破棄させて変更を即座に反映させることができる
- ちなみに、Assets Pipelineは画像もサポートしている
- 変換や結合を行うわけではないが、ファイル名にハッシュ値が付くので、キャッシュ対策ができる
- ビュー内で<%= image_tag "rails.png" %>とすると、app/assets/images/rails.pngを表示できる
「JS/CSSにコンパイルされるメタ記法」が簡単に使えるようになった
- 例:
- application.css.scss -> applicaiton.css
- application.css.sass -> applicaiton.css
- applicaiton.js.coffee -> applicaiton.js
- SCSSとは:
- Hamlプロジェクトの成果物だけど、本体より有名になってないか
- SASSとは:
- HamlのCSS版
- インデントベース
- CoffeeScriptとは:
- JSにコンパイルされるインデントベース言語
- だいたいJSと同じだが、いろいろ改善されてる
- 「==」が「===」にコンパイルされるとか
- リスト内包表記があるとか
- 「.bind(this)」が簡単に書けるとか(=>)
- Rails3.1のscaffoldは、なんとscssとcoffeeが標準で生成される
- coffeeのコンパイルにはJS環境が必要だが、Win/MacではOSに入ってるJS環境を使うらしい
- もちろん生の.cssや.jsを使うこともできる(foo.css, foo.jsを置くだけ)
- foo.js.coffee.erbみたいなこともできる(!)
- でもfoo_new_path()とかは使えなかった(なんで?)
コントローラ単位でjsファイルを分けて書くようになった
- 例:codes.js.coffee, user.js.coffee
- JS書くときの見通しが良くなった
- どれだけファイルを分けても、productionでは1ファイルに結合されるので読み込みのオーバーヘッドを心配する必要なし
- (逆に言うと、特定コントローラでだけ読み込まれるというわけではないので注意)
実装
- Rails 3.1ではsprocketsというgemが標準採用された
- (スプロケット:歯車の一種、自転車の後輪についてるやつとか)
- 「//= require foo」で、foo.jsやfoo.cssがそこに埋め込まれる
- Railsではapp/asserts/(javascripts|stylesheets)/application.(js|css)にrequireが書いてある
Rails 3.2
2011/12/20 Rails 3.2 RC1が出た
Riding Rails: Rails 3.2 RC1: Faster dev mode & routing, explain queries, tagged logger, store
高速化と機能追加が主で、大きな(= アナウンスに書くほどの)変化はないらしい
- development modeでの再読み込みが速くなった
- routerが速くなった
- Journey
- aaronさんがアルゼンチンで発表してた
- ARelでSQLのexplainを簡単にできるようになった
- puts Person.active.limit(5).explain
- see also
- ロガーにタグを付けられるようになった
- Logger.tagged("foo"){...}とやると、"[foo] ..." みたいなログになる
- Active Record Store
- 謎機能
- ARをkey-value storeみたいに使えるらしい
- 実装的には、ARのserializeメソッドでHashを(デフォルトではYAMLで)文字列化してtextカラムに保存する模様
Rails 4
git masterは既に4.0.0.betaらしい(つまり3.3は出ない)
Riding Rails: Rails/master is now 4.0.0.beta
4.0.0の主な変化:「Ruby 1.9専用になる」(1.8.7のサポート終了)
逆に言うと、1.8サポートを終了するのでバージョンを4.0に上げた
なので、Rails 2→3ほどのbig changeではない
Railsは今のところだいたい2年サイクルらしい
- 2005: 1.0
- 2007: 2.0
- 2010: 3.0
- 2012: 4.0? (早ければ夏頃)(予定は未定)
■ [rails][memo] sprocketsのJavaScriptテンプレートサポート
- sprocketsのREADME見たら、JavaScript Templatingもサポートしてるらしい
# templates/hello.jst.ejs にこう書いておくと:
<div>Hello, <span><%= name %></span>!</div>
# application.js から、JSTという変数経由で読み込めるらしい
//= require templates/hello
$("#hello").html(JST["templates/hello"]({ name: "Sam" }));
# jstはRubyを使ってサーバ側でjsにプリコンパイルされる
しかしちょっと待て、EJSの構文はERB風だけど、CoffeeScriptはインデントベースではないか?
どうするのかと思ったら、CoffeeScriptでもend書かせる仕様だった…
<% if @project.isOnHold(): %> On Hold <% end %>
このへんslim風の構文にすれば上手くいくんじゃないのかなぁ。記法がslimで、コード部分だけRubyじゃなくてCoffeeScriptになってるの。
jadeでなんとかなるのかな?と思ったけど、あれはNode.jsでサーバサイドで使うものだから違うと言えば違う。Rubyで動かないし(と言って、Rails環境にJS処理系が入ってたりする昨今だけど)。
(しかしRailsユーザもCoffeeScript使うようになると、サーバサイド(Node.js)におけるcoffeeの話と、クライアントサイドにおけるcoffeeの話が混ざって大変だな。)
ふつうJSテンプレートっていうと、クライアント側でJS使って展開する ようなライブラリが多いと思うけど、確かにサーバサイドでプリコンパイルした方が速そうではある。

□ leque [これが動いたり動かなかったりするのは begin の問題ではなくて、 hygienic macro とトップレベルの..]
□ yhara [ありがとうございます。寝る前にベッドで考えてて、「syntax-caseを使えばいけるのではないか?」というところま..]