Syntax highlighter

2014-02-28

読書感想文(All You Need Is Kill)

となりのヤングジャンプとヤングジャンプで連載されてる漫画の原作。漫画読んで原作を読みたくなったのはかなり久しぶりである。5月に日本に帰るのでそのときに買えばよかったような気もするが、はやる気持ちは1年前にもらったギフトカードを使ってAmazon.deでの英語版の購入を後押しした形になる。

核心には触れない形で書くつもりだが、うっかりネタ晴らししてしまっていてもご容赦いただきたい。

とりあえず読んだ感想としては買って損はなかったになる。 日本なら書籍でも600円で買えるのだが、英語版は€10とほぼ2倍の値段である。日本で買える環境にあるなら日本語版をお勧めする。英語の勉強用?多分やめた方がいい。Yonabaruの会話文がえらく訛っているし、そこそこ難しい単語(1ページに1は知らん単語があった)が出てくるので楽しめないと思う。ただ、訳者がよかったのか、元がいいのか、はたまた両方なのかは分からないが個人的には読みやすかったという印象ではある。多分、SiFi系の小説にありがちなこてこての背景説明が少なかったというのもあるのだろう。


Wikipediaのページにある登場人物の項目でShastaがでかでかと載っているのだが、出番は少なめ。眼鏡オタク娘好きにはものたりないかもしれない。ShastaよりYonabaruの方がよっぽど登場回数多いのに載っていないのは何故だ?

Rita Vrataskiという名前からロシア系なのかなと勝手に思っていたのだが実はアメリカ、イリノイ州出身という。Ritaって名前にアメリカ人という印象がないのでいろいろやられた感はある。このページによると0.204%程度らしいのでそもそも珍しいのだろう。読み飛ばしたのかもしれないのだが、Ritaの本当の苗字ってなんだったんだろう?

ギタイ(Mimic)の命名由来も出てこなかった気がする。作中では膨らんだ蛙(bloated frog)と表現されていたが、漫画版ではそんな風には見えない。彼らが人間と戦争している理由に関しては、個人的にだが、ちょっとチープな感じがした。SiFiなのでまぁありなんだろうけど、唐突だなぁ感が拭えなかった。

今年の6月に映画がでる。題はEdge of Tomorrow。なぜ変えたし?Tom CruiseがKiriya Keiji役(役名William "Bill" Cage) なのだがどう見ても20台前半の新兵に見えない件。Rita Vrataski役のEmily Bluntもどう見ても22歳(実際には多分2つか3つ若い設定)に見えない件。原作は舞台日本で主人公日本人なんだけど、そこはアメリカ映画(Warner Brosってハリウッド?)、全部アメリカになってる。Trailer見ると面白そうではあるので見に行くかもしれない。

あまり書くとネタ晴らししそうなのでこれくらいにしておく。

2014-02-26

R6RSのマクロ展開フェーズ

日本語で解説してる記事があまりにも少ないのと、これを毛嫌いしてる人が多いので何か書いてみる。随分前にチラッと書いてたりするが、単なる調査用の記事だったのである程度まじめに解説する。英語だとこれが詳しい。

はじめに、なぜフェーズなどというものが必要か?
R6RSでは低レベルマクロであるsyntax-caseがある。これはdefine-syntax内で内部定義を可能にする。マクロとはコンパイラがコンパイル時に(もしくはそれ以前)に定義に従って式を別の式に展開するのだが、以下のようにマクロ定義内に別のマクロ定義があるとそのマクロは何時展開するのかということが問題になる。
#!r6rs
(import (for (rnrs) run expand))

(define-syntax foo
  (lambda (x)
    (define-syntax bar
      (syntax-rules ()
        ((_) #''foofoo)))
    (syntax-case x ()
      ((_)
       (with-syntax ((name (bar)))
         #'name)))))
(foo) ;; -> foofoo
これを解決するのがフェーズというわけである。マクロbarはマクロfooが展開される前に展開される必要がある。ではbar内で使われているsyntax-rules等の名前を解決する必要がある。フェーズとはその名前解決が行われる段階を明示的に指定したものと思えばよい。runとexpandが名前つきで提供されているが、これらは(meta 0)と(meta 1)の別名である。

フェーズとはマクロ内マクロで使われるものである
間違いを恐れずに言い切ってしまえば、フェーズとは上記の場合にのみ考慮に入れる必要があるものだ。もちろん他のライブラリで定義されたマクロもこれに含まれる。

メタレベルの必要性
R6RSのマクロフェーズにはメタレベルなるものがある。デフォルトのrunとexpandで足りない場合はユーザが(meta 2)等適当に指定することができる。 これは本当に必要なのか?結論を言えば必要である。メタレベルはマクロ内マクロが多段になると必要になる。以下のコードがそうだ。
#!r6rs
(import (for (rnrs) run expand (meta 2)))

(define-syntax meta0
  (lambda (x)
    (define-syntax meta1
      (lambda (x)
        (define-syntax meta2
          (lambda (x)
            (syntax-case x ()
              ((_) #'(generate-temporaries '(a))))))
        (syntax-case x ()
          ((_) 
           (with-syntax (((name) (meta2)))
             #'#'name)))))
    (define (gen-name) (meta1))
    (syntax-case x ()
      ((_)
       (with-syntax ((name (gen-name)))
         #'(display 'name))))))


(meta0) ;; -> prints temporary symbol
見た目に分かりやすいようにマクロの名前は各メタレベルにしてある。こんなコード書くやついねぇよ!と思うかもしれないが、例示できるコードレベルで出るということは必ず誰かは使うということである。明示的か暗黙的にかは別にしてもだ。

R7RSではどうなるか?
R7RSもlargeではexplicit renamingが取り込まれる方向に進むと思われるのだが、まだ議論すら始まっていない状態なのでなんとも言えない。ただ、フェーズは毛嫌いしてる人が多い(自分含む、要出展)のとR7RSが提供するdefine-libraryにはフェーズ指定をするキーワードは提供されていないので、暗黙の内に解決する方向に行くのではないかと思っている。

2014-02-16

SRFI-5の紹介

(LISP Library 365参加エントリ)

SRFI-5はletの拡張SRFIです。正直使ったことない上に、Sagittariusでは0.5.2になって(ほぼこの紹介記事を書くためだけに)サポートされたものだったりします。なので、ちょっと触ってみたレベルで紹介記事を書きます。

このSRFIは既存のlet、特に名前つきletの拡張を行います。具体的には新しいフォームとオプショナル引数を受け付けるようになります。具体的には名前付きletが以下のよう
(import (srfi :5))

(let (loop (i 0))
  (when (< i 10)
    (display i) (newline)
    (loop (+ i 1))))
loopの位置が束縛の先頭にはいるという感じです。(正直、僕にはエディタの恩恵が受けづらくなるだけに見えるのですが・・・)

オプショナル引数は以下のようにして受け取ります。
(import (srfi :5))

(let (loop (x 0) (y 1) . (z 2 3 4))
  (if (list? x) 
      (list x y z)
      (loop z x y)))
オプショナル引数は複数個の値がリストにパックされます。また通常のletフォームもサポートされるので、通常の名前付きlet+オプショナル引数という感じでも書くことができます。

実際にどういう仕組みで動いているかというのは、マクロの展開形を見ると分かります。
((letrec ((loop (lambda (x y . z) 
                  (if (list? x) 
                      (list x y z) 
                      (loop z x y))))) 
   loop) 0 1 2 3 4)
納得の展開結果ではないでしょうか?

とりあえず触った感想なのですが、名前付きletにオプショナル引数がほしいという場合は使えるのではないでしょうか。個人的にはそういったケースは非常に少ない(もしくはない)ので多少適当な紹介になってしまった感があります。

2014-02-14

Sagittarius Scheme 0.5.1リリース

Sagittarius Scheme 0.5.1がリリースされました。今回のリリースはメンテナンスリリースです。

修正された不具合
  • マクロの可視性に関する不具合が修正されました
  • datum->syntaxによって生成されたシンボルが非可視になる不具合が修正されました
  • いくつかのポートに対する手続きに閉じたポートを与えるとSEGVは発生する不具合が修正されました
  • 不正なdefineが例外を挙げない不具合が修正されました
  • (tlv)ライブラリで長さのエンコーディングに対する不具合が修正されました
  • コンパイラにcircular listを渡すと無限ループに陥る不具合が修正されました
  • formatにカスタム文字ポートを渡すとSEGVが発生する不具合が修正されました
新たに追加された機能
  • R7RSスタイルのSRFIライブラリがサポートされました。例:(srfi 1)
  • SRFI-5が追加されました
  • 局所メソッドフォームlet-methodが追加されました
  • R6RSのレコードが組込みCLOSに統合されました
改善点
  • quasiquoteが可能であれば定数を生成するようになりました
  • コンパイラが可能であればコンパイル時に手続きの呼び出しをするようになりました
新たにサポートされた環境
  • OpenBSDでのビルドが可能になりました

2014-02-13

Introduction of JSON tools for Scheme

I'm writing a library which can query JSON. It's still under development state but a bit of sample code wouldn't hurt so let me introduce it.

The repository is here, json-tools and handling JSON structure must be one of the Chicken Scheme egg's library json format. (I've ported it for R6RS Scheme implementations, it's in the repository as well.)

The library consists 2 parts one is JSON tools and the other one is JSON query which is based on the JSONSelect.

JSON Tools

This part of the library is highly inspired by SXPath. There are bunch of basic selectors which can be used by querying libraries. Following piece of code describe the flavour of this part.
(import (rnrs) (text json tools))

(define json '#(("name" . #(("first" . "Lloyd") ("last" . "Hilaiel")))
                ("favoriteColor" . "yellow")
                ("languagesSpoken"
                 #(("lang" . "Bulgarian") ("level" . "advanced"))
                 #(("lang" . "English")
                   ("level" . "native")
                   ("preferred" . #t))
                 #(("lang" . "Spanish") ("level" . "beginner")))
                ("seatingPreference" "window" "aisle")
                ("drinkPreference" "whiskey" "beer" "wine")
                ("weight" . 156)))

((json:filter (lambda (node)
  ;; The given node can be other type and
  ;; this piece of code may raise an error
  ;; but for above structure this works :)
  (equal? "name" (json:map-entry-key node))))
 (json:child-nodes json))
;; -> <json:nodeset>
All JSON selectors return JSON nodeset type which contains sets of JSON node. Followings are the JSON node types;
  • JSON map
  • JSON map entry
  • JSON array
  • JSON string
  • JSON number
  • JSON boolean
  • JSON null
All types are sub type of JSON node. The reason why I introduced them is that there was no way to tell the difference between array and map entry which contains array. To avoid ambiguous results, I needed to do it.

To retrieve the result set as S-expression, you can simply call the `json:nodeset->list` procedure like this;
(json:nodeset->list ((json:filter (lambda (node)
                                    (equal? "name" (json:map-entry-key node))))
                     (json:child-nodes json1)))
;; --> (("name" . #(("first" . "Lloyd") ("last" . "Hilaiel"))))
Not sure if the procedure name is proper. (I also though `json:nodeset->sexp`.) To casual use, `json:filter`, `json:child-nodes`, `json:child-nodes-as-list`, `json:child` or `json:child-as-list` are easy to use. The rest of selectors are a bit tricky.

JSON Select

This part of the library is for usability. You can use query language to select  particular nodes from JSON. The example use is like this;
(import (text json select))

;; use the same JSON structure defined above
((json:select ".languagesSpoken") json)
;; --> <json:nodeset>

(json:nodeset->list ((json:select ".languagesSpoken") json))
;; --> '(("languagesSpoken"
;;        #(("lang" . "Bulgarian") ("level" . "advanced"))
;;        #(("lang" . "English")
;;          ("level" . "native")
;;          ("preferred" . #t))
;;        #(("lang" . "Spanish") ("level" . "beginner"))))
The returning value is JSON nodeset the same as tools. So again to retrieve the S-exp result, you need to call the procedure `json:nodeset->list`. Currently, not all query language are implemented but it would be a matter of time.

As I mentioned before, the state is still under development so your opinions or testing results are very welcome :) Of course, your pull requests are much appreciated :)

2014-02-10

Integrated R6RS record to CLOS

I have made a sort of huge change for Sagittarius in these couple of days and that is now R6RS record can be used with generic functions. So let me show what's the exact change for this.

Sagittarius had 2 type systems, one is CLOS and other one is R6RS record and these didn't have any compatibility. So following code were invalid.
(import (rnrs) (clos user))
(define-record-type (pare kons pare?)
  (fields (mutable x kar set-kar!)
          (immutable y kdr)))

(define-method write-object ((p pare) out) 
  (format out "#<pare ~s:~s>" (kar p) (kdr p)))

(print (kons 1 2))
This was because R6RS record didn't create CLOS class but own record type. I was thinking this is very inconvenient and made me not to use R6RS record. So I have made the change and now above code works as I expect.

Followings are what I've changed internally so may not be so interesting.

[Slot ordering and shadowing]
I needed to change computed slot order of a class from subclass slots followed by super-class slots to super-class slots followed by subclass slots. And to make R6RS record spec satisfied, made not to shadow any duplicated slots. Following code describes a lot;
(import (rnrs) (clos user) (clos core))

(define-class <a> () (a b))
(define-class <b> (<a>) (c d))
(define-class <c> (<a>) (a c d))

(print (class-slots <b>))
;; ((c) (d) (a) (b)) : 0.5.0 or before
;; ((a) (b) (c) (d)) : current

(print (class-slots <c>))
;; ((a) (c) (d) (b))     : 0.5.0 or before
;; ((a) (b) (a) (c) (d)) :current
So basically no eliminating slots and different order. Then I had immediately noticed the problem that this breaks slot accessing. For example, refering <c>'s slot 'a' may return <b>'s 'a' slot value. The solution was very easy. The bottom most class's slots need to be shown first this means searching reverse order was sufficient.

The benefit of this change is big. Accessing slot with class is now always returns proper position of slot. Slot accessor contains index of the slot position and the change made this position always the same no matter how many you extend classes. For above example, position of class <a>'s slot 'a' is always 0 and before this wasn't always 0 (obviously...). Additionally, slot accessor also contains the class information that indicates on which class it's defined.

[Defining procedual layer in Scheme]
I have made a small footprint for this integration with CLOS. And based on this code, I've implemented procedural layer in Scheme world so that those ugly C code for record could be removed.

The Scheme implementation creates a CLOS class per record type descriptor(RTD) and set it to RTD and visa versa. This could save me from a lot of troubles such as refering record constructor descriptor (RCD) from record type. (I think there is a better solution but I was lazy.) There is a small problem with current Scheme code, that is it is impossible to create more than 1 record constructor descriptor from one record type descriptor. I may fix this if it will be a problem but highly doubt it.

[Record type meta class]
To distinguish between normal CLOS instance and record instance, I needed to create an additional meta class for it. However, it was pain in the ass to create meta class in C level so I decided to extend current class structure to have RTD and RCD fields and not to show the slot in but . This makes memory efficiency slightly bad (2 words) but I don't think it's a big deal.

SRFI-4の紹介

(LISP Library 365参加エントリ)

SRFI-4はSchemeで整数データ、及び浮動小数点専用ベクタを扱うSRFIです。 用意されているデータ型としては、u8、s8、u16、s16、u32、s32、u64、s64、f32及びf64です。最後二つ以外は数値の整数ビット+符号を表し、f32とf64は浮動小数点数のビット数を表します。

それぞれのデータ型に対して以下の処理が定義されています。(SRFIに習ってデータ型をTAGと表示しています。)
  • (TAGvector? o)
  • (make-TAGvector n [ TAGvalue ])
  • (TAGvector TAGvalue ...)
  • (TAGvector-length TAGvect)
  • (TAGvector-ref TAGvect i)
  • (TAGvector-set! TAGvect i TAGvalue)
  • (TAGvector->list TAGvect)
  • (list->TAGvector TAGlist)
また、それぞれのデータ型に対してのリーダマクロも定義されています。

具体的な使用例を見てみましょう。動作確認処理系はSagittarius 0.5.0です。
#!read-macro=srfi/:4
(import (srfi :4))

(define s8 #s8(0 -1 2 -3 4))

(s8vector-ref s8 1)           ;; => -1
(s8vector-set! s8 0 5)        ;; => unspecified
(list->s8vector '(1 2 3 4 5)) ;; => #s8(1 2 3 4 5)
(s8vector->list s8)           ;; => #s8(5 -1 2 -3 4)
R6RS以降バイトベクタが基本データ型に入ったのでこのSRFIの存在意義が薄れた感はありますが、R5RSで書かれたスクリプト等に使われている場合があるので知っておいても損はないかと思います。

こんなSRFIないの?的なリクエストをお待ちしてます。(順番どおりにやると使用頻度低めのSRFIが続くのでw)

2014-02-06

マクロのバグを非常にひどい方法で解決したのでメモ

どこかにメモしておかないと忘れるw

一つ前の記事でaifのitがマクロにくるまれるとうまく参照できないというバグの話をしたが、一応直したのでメモ(というか、FIXME的な何か・・・)。

どうしたか。
問題はマクロ展開時に生成される識別子の環境が不十分なために参照する際に大雑把にしか(というと語弊があるが)識別ができなかったこと。ただ根本を解決せず搦め手で直してしまった。
このit識別子を参照可能にすると、syntax-rules内でネストした識別子の誤参照が置き、不可能にすると、まぁitが見えないという状況に置かれたので、とりあえずコンパイル時環境に何が入っていて何を見つけないといけないかを大まかに見てみたら、参照可能にした場合は同一の識別子が後ろにあるにもかかわらず一つ二つ前のものを誤参照していた。そこで、一度全部環境を舐めてから見つからなかった場合にそれっぽいものを返すという荒業を使うことにしている。もちろんバグの匂いしかしないw

根本的には全く解決されていないので、正しくは環境(恐らく)テンプレート変数のコピーをする際にオリジナルの環境をそのまま使うのではなく、何かしらマークを入れてやり識別可能にするという方法を取るべきなのだろう。ただ、何をどうすればいいのかさっぱり分かっていないので、どうしたものかという感じ。

2014-02-03

Schemeのマクロにおける変数参照についてのメモ

自分の考えをまとめるためのメモ。主にSagittariusでの実装の話。

Schemeは変数のシャドウイングがある。そのためコンパイラが適切に変数を参照するためにはそれがどこで束縛されたかを知っている必要がある。これだけなら環境はスタックのように束縛された変数を持っておき、上から順に探索すればいいだけなのだが、問題になるのはhygenic macroで束縛された変数(便宜上テンプレート変数と呼ぶ)である。

例えば以下のケース
(define-syntax test
  (syntax-rules ()
    ((_ a b)
     (let ((a a))
       (+ a b)))))
(let ((a 1) (b 2)) (test b a))
というのは、3を返す必要がある。(今一いい例ではないかもしれないが)上記の例ではaがテンプレート変数ということになる。Sagittariusではテンプレート変数は基本的にリネームされ、実際に渡される式とは別の扱いになっている。リネームの再に現在のマクロ環境が用いられてリネームされるので生成される識別子aはこの場合は空の環境を持ったものになる。(実際にはマクロであることを識別するためのマークと定義されたライブラリの情報が入る)

この辺りまでならまだ混乱は少ないのだがR6RSにはdatum->syntaxがある。これが頭の痛い問題で、syntax-rulesでは不可能な「識別子が定義される環境を任意の場所に指定する」ことができる。例えば、CLで有名なaifは以下のようになる。
(define-syntax aif
  (lambda (x)
    (syntax-case x ()
      ((aif expr then else)
       (with-syntax ((it (datum->syntax #'aif 'it)))
         #'(let ((it expr))
             (if it then else)))))))
(aif (car '(a)) it #f) ;; => a
上記の例ではitはマクロaifが定義された環境(つまり空)を持つ識別子となる。そうすることでaif式内で参照されるitはあたかもグローバルに束縛された変数を参照するような挙動をする。しかし、現状の実装では以下のような場合に上手く動かない。
(define-syntax wrap
  (syntax-rules ()
    ((_)
     (aif 'ok it #f))))
(wrap) ;;=> should return ok but raises an error
一段マクロをかますことで、itがテンプレート変数に変更され環境が変わるからである。本来であればitは大域で定義された変数と同様な動きをするべきだが、そうなっていない。(wrapが局所で定義されたらどうするんだという話もあるがとりあえず放置・・・)

細かいケースを上げたらきりがないのだが、本題としてはどの場合にどの識別子を同一のものとみなすかということである。例えば同じ環境を保持しているのか、コンパイラ環境に格納された変数が、ターゲットの変数と環境を共有していればいいのか、とかそんな感じである。現状の実装ではマクロの定義時と展開時にリネームが走るのだが、その部分もおそらく見直す必要がある。余計な情報を付加しているか、または逆に情報の欠落が起きている可能性があり変数参照時に正しく参照できていないからである。

マクロのバグを踏むたびに既存の展開器を使っておけばよかったなぁと思ったりもするが、それだと面白くないというのもあったりで複雑な気分である。

2014-01-24

SRFI-2の紹介

(LISP Library 365参加エントリ)

SRFI-2はand-let*と呼ばれるマクロを提供する。例えば以下のようなコードを書いたことはないだろうか?
;; make upcase symbol of car part if it's symbol
(and (pair? o)
     (let ((a (car o)))
       (and (symbol? a)
         (let ((s (symbol->string a)))
           (cons (string->symbol (string-upcase s)) (cdr o))))))
andとletがネストして非常に読みにくい素敵コードだ。こんな不快深い階層のネストを解消してくれる構文がand-let*である。これを使うと上記のようなコードは以下のようにすっきりと書ける。
(and-let* (( (pair? o))
           (a (car o))
           ( (symbol? a) )
           (s (symbol->string a)))
  (cons (string->symbol (string-upcase s)) (cdr o)))
あまり変わらない?それはあなたの心が澱んでいる可能性が高いのでリフレッシュさせることをお勧めする。

オリジナルの実装はdefine-macroで作られているのでCLに移植するのも難しくないはずだ。実際既に作られている

2014-01-20

風車の中

ライデンにはオランダ最古(だったはず)の風車があるのだが、長年外から眺めるだけで中に入ったことはなかった。先日偶然にも開いていたので(もちろん有料だけど)、せっかくだと思い中に入ってみた。

とりあえず、写真
展望台(?)から臨むライデンの街並み(一眼レフとかだともう少しワイドに取れて様になった気はするが気にしないw)

展望台から取った風車部分。

今は使われていない粉引き(?)

その2

信じられるか?これ風車の中なんだぜ?

立派なもんだろう?

正直自分のアパートよりはるかに豪華で泣けるw

 さすがにキッチンは時代を感じさせる。

ちなみに、風車自体は1802年に建築されたものらしいく、結構オーナーが転々と(多分子孫だと思うけど)していた。内装はおそらく当時、もしくは最終所有者が手放した状態のままだと思われる。まぁ、さすがに電気と水道は近年のものだと思うけど。写真は一階部分の居住区で2階以降は博物館になっていた。まぁ、これ自体が博物館みたいなものだが。

大人4ユーロと多少高めな入場料ではあったが、割と満足できると思う。機会があればぜひその目で確かめてほしい。

2014-01-17

Sagittarius Scheme 0.5.0 リリース

今回のリリースはマイナーバージョンアップデートリリースです。また、リポジトリ及びダウンロードの場所が変更されたので注意して下さい。ダウンロード

修正された不具合
  • importがcond-expand内でR7RSが要求するように動かなかった不具合が修正されました
  • Linux上でプロセスを継続的に起動するとクラッシュする不具合が修正されました
新たに追加された機能
  • change-classが追加されました
改善点
  • ライブラリファイルの拡張子を追加する-Sオプションが追加されました
  • クラスがdirect-subclassesスロットを持つようになりました
  • R7RSスタイルのSRFIライブラリ名がサポートされました
  • コンパイラがいくつかの参照透過かつ末尾位置にない式を削除するようになりました
内部的な変更
  • 完全なセルフホスティングになりました

2014-01-16

よろしいならば戦争だ(マクロ編)

もう何度目の戦だか覚えてもいない。

CLOS周りで微妙なkludgeを使っているのが嫌になって、えいやっと書き換えてやれと思ったらバグを発見したという話。

再現コードとしては以下のようなの。
(import (rnrs))

(define-syntax aif
  (lambda (x)
    (syntax-case x ()
      ((aif expr then else)
       (with-syntax ((it (datum->syntax #'aif 'it)))
  #'(let ((it expr))
      (if it then else)))))))
(define-syntax wrap
  (syntax-rules ()
    ((_)
     (aif 'ok it #f))))


(wrap) ;; => error
(wrap)はokを返さないといけないんだけど、itがないって怒る。まぁ、原因も実は分かっていて、wrap内でitはテンプレート変数に変換されるんだけど、これに変換されるとコンパイラがうまいこと局所変数を参照できないという問題だったりする。

正直現在の実装は中身がぐちゃぐちゃになりすぎてて、何をやっているのかがコメントと記憶を頼りにするしかないのだが、このパターンは単純にもれている気がする。ただ、どうすればいいのかというのが今一分かっていない。例えば、上記の場合ならコンパイラが参照する環境に含まれる局所変数は識別子になっているので、識別子+テンプレート変数の組み合わせを参照する際に何かすればいいような気がするが、これが何かを壊すのではないかという不安もあって、う~んといった感じである。

まぁ、一つ分かっているのは、これは多分明日のリリースには間に合わないということかw

2014-01-15

日本語 vs English

先日一年ぶりくらいに日本語を話す機会があったのだが、その際に不思議な感覚を覚えたので書いて見る。はっきり言って個人の日記レベルである(ちょっと前にTwitterで流行ってたので使ってみるw)

不思議な感覚というのは、日本語を喋っているときに英単語を言おうとするとカタカナ発音に敢えて変換していたこと。敢えてというのは、無意識にカタカナの発音を検索してから発音していたという感じ。検索してること自体はなんか意識下にあったんだけど、検索する作業が無意識に発生していた。これ、逆のパターンのときの実はあって、英語話してるときに日本語の単語を言えといわれるとなんか片言みたいな発音になっていたりする。

言語別のコンテキストがはっきりと分かれてきたのかなぁと思わなくもない。が、そう入っても普段の頭の中は基本日本語だし、スイッチ入れ替えるのも特にストレス感じることなく行えるので、わざわざ検索しているという感覚がなんとなく奇妙で不思議だったりしたわけだ。

言語別のコンテキストといえば、混ぜるな危険ではないが、英語話してるときに日本語で考えていないし、逆も(というか逆はある意味当たり前だが)然りな感じである。その昔、後天的バイリンガル(という言葉が正しいかは知らない)は母語が複数ある人と比べると同じ言語でも使っている脳の位置が違うなんてのをテレビか雑誌で見た記憶があって、なんとなくそんな感じになってきたのかなぁと思ったりもした。

特にオチもなく終了。こういう現象に名前付いてないのかね?

2014-01-14

SRFI-1の紹介

(LISP Library 365参加エントリ)

SRFI-1の紹介。このSRFIを使ってないSchemerはいないんじゃないかなぁと思われるくらい有名なSRFI。内容は便利なリスト操作手続きをまとめたもの。

今回は、便利だけどあまり日の目を見ない手続きに焦点を当てて紹介する。題はSRFIの目次に対応している。

Constructors

list-tabulate
iotaを知っている人は多いと思うが、list-tabulateも同様にリストを構築する手続き。以下のように使える。
(list-tabulate 5 values) ;; => (0 1 2 3 4)
第一引数にリストの要素数を受け取り、第二引数にリストのn番目の要素を構築する手続きを受け取る。上記の例だとiotaの方が短い記述でかけるが、数値以外の要素を作りたい場合には便利になる。

Miscellaneous

append-reverse
append-reverse!
見れば分かるような気がするが、(append (reverse lis) tail)のシノニム。後者reverse!を使うので、注意が必要。
(append-reverse (list 1 2 3) 4) ;; => (3 2 1 . 4)

Fold, unfold & map

append-map
append-map!
append-reverseと似たようなのだが、(apply append (map f lis1 ...))をするもの。リスト内リストを操作しつつフラットにするのに便利。
(append-map values '((1) (2) (3))) ;; => (1 2 3)

filter-map
(filter values (map f lis ...))をするもの。fが#fを返すような場合に返されるリスト内から#fを取り除く。
(filter-map (lambda (n) (and (number? n) n)) '(1 a 2 b 3 c)) ;; => (1 2 3)
注意が必要なのは、fの戻り値がリストの要素になるということ、なので以下のようなものは悲しい結果になる。
(filter-map even? '(1 2 3 4 5 6)) ;; => (#t #t #t)

Deletion

delete-duplicates
delete-duplicates!
リスト内から重複するリストを削除する。
(delete-duplicates '(a a b b c c)) ;; => (a b c)
(delete-duplicates '((a b) (c d) (a b) (e f) (c d)) equal?) ;; => ((a b) (c d) (e f))

ここに列挙したのは僕が便利だと思うものだけである。他にも便利そうな手続きがあるので気になったら眺めて使ってみるといいかもしれない。

2014-01-12

SBCLのPCLを読む

クラス再定義を実装するべPCLを参考にすることにした。ので、真面目に全部を読むわけではなく、defclassから下に続く処理を読むだけ。また、断定系で書いてあるけど、動作確認までしてるわけではないので、全ての断定系には「と思う」を付与して読むこと。上から順番に何をしているか書いてるので、読み終わるころにはPCLのdefclassが何をしているのか分かる(と思う、補足して読むことw)。

defclass
単なるマクロ。一応defstructで定義されたものも裁けるようになってるらしい。展開後はload-defclassを呼ぶ式になる。

load-defclass
defclassは単なるマクロなので、マクロ内で何かをするか、展開結果がクラスを作ることは容易に想像できると思う。じゃあ、その下請けの関数は何か?という話。
2種類あって、単にコンパイラに知らせる用のものと、実際にensure-classを呼ぶreal-load-defclassがある。PCLをブートする再には前者を使って、ブート後は後者を使う。

ensure-class
ロックをかけて渡されたクラスの名前からクラスを検索、ensure-class-using-classを呼ぶ

ensure-class-using-class
メソッド。ensure-classでクラスが見つかった場合と見つからなかった場合の2種類が定義されている。
前者はfrob-ensure-class-argsを呼んで見つかったメタクラスが再定義されるクラスを同じならchange-classを呼ぶ、違えば呼ばない。その後、reinitialize-instanceを呼んでインスタンスを更新する。メタクラスが違った場合はどうなるんだろう?
後者は単に普通の定義。(以下では言及されない)

change-class
メソッド。いくつか定義されてるけど、基本的にはCPLを調べて変更可能かのチェックをしたのち%change-classを呼ぶだけ。

%change-class
古いインスタンスと新しいインスタンス、このケースでは元クラスと新クラス、を受け取ってメモリ割付、スロットとメタクラスの交換をした後、update-instance-for-different-classを呼ぶ。

update-instance-for-different-class
基本的には何もせずshared-initializeに処理を委譲

shared-initialize
メソッド。プライマリのメソッドは一個なんだけど、クラス用に:beforeと:afterが定義されてる。(それ以外にもあるが。) 中身は後で読む。っが、見た感じ、クラスのスロットを詰めてるだけに見える。まぁ、initializeからも呼ばれるメソッドなので、ある意味当たり前か。

reinitialize-instance
メソッド。 プライマリはcheck-initargs-1呼んで何かしらチェックした後、shared-initializeを呼ぶ。
クラス用の:beforeではダイレクトサブクラスを除去したのちスロットの除去をしてる。
:afterではmap-dependentにupdate-dependentを呼ぶ手続きを渡している。何するかは今一不明。多分依存関係の解決。

基本的にはほぼ全ての手続きがメソッドなので、頑張ればいろいろ手を加えられそう。Sagittariusに組み込む場合ここまでは要らないので、下請け手続きは単なるlambdaにする気がする。

2014-01-11

Scheme処理系の選び方

世の中に星の数ほどSchemeの処理系はあれど、その選び方についてはあまり言及されていない気がするので、目的、環境別くらいの処理系の選び方を書いてみようと思う。僕の知っている処理系の話になるのでかなり限定されたものになることには目をつぶってほしい。また、基本リリースされているバージョンについてのみ言及なので、この処理系の開発版はサポートしているというのは割愛されている可能性があることにも注意してほしい。さらには、少なくともRnRS(R5RS以降)に準拠している処理系のみの言及であることも留意してほしい。

目的別

【SICP用】

 どれでもいいw 環境別辺りを参照して適当に選んでw

【R7RS準拠の処理系を使いたい】

現状でChibi SchemeとSagittariusのみがほぼ完全にサポートしている。
うわさに寄るとChicken Schemeもサポートしているらしいのだが、最新バージョン(4.8.0.5)ではされていなかった。
Gaucheは0.9.4でサポートされる予定。
Kawaが意欲的にサポートしているらしい。

【R6RS準拠の処理系を使いたい】

この辺参照。ただし、Biwa SchemeはR6RSのサポートが弱いので(syntax-caseとか)注意が必要。

【とにかく高速な処理系】

Vicare、Larcenyが機械語にコンパイルする(はず、未使用、未確認)
(ただし、VicareはLinuxじゃないと処理系自体がインストールできない)
RacketはJITがあるので特定の環境(x86等)では高速
ChickenはCへのトランスレータがあるのでCコンパイラの最適化による
Chezも商用版は機械語にコンパイルするらしい

LarcenyはCへのトランスレータもあるらしい

【FFIが使える処理系】

Chez、Chicken、Racket、Vicare、Mosh、Ypsilon、Sagittarius。そこそこ実用的な処理系はほぼ持っているはず。
Mosh、Ypsilonはx86、x64限定。SagittariusはlibFFIを使っているのでかなりのCPUでFFIが使える。
(自分の処理系の宣伝w)

【ライブラリが豊富な処理系】

RacketのPLT、ChickenのEgg等

【ドキュメントが充実してる処理系】

Racket、Chicken、Gaucheはドキュメントが充実している。
Sagittariusもまぁまぁ。
(あまり他の処理系のドキュメントを参照しないのでこの項目は弱いw)

【いざというときに日本語で質問できる処理系】

Gauche、Mosh、Sagittarius、Ypsilon、Schluesselは開発者が日本人。
多分他にもあるがよく知らない。

環境別

【Windowsでインストーラ一発インストールしたい】

Chez、Racket、Sagittarius、YpsilonはWindows用のインストーラがある

【Mac OSで使いたい】

Chicken、Racket、Mosh、Chibi、Gauche、Sagittarius等結構ある、がBrewに登録されてるのはどれかは知らない。

【Linuxで使いたい】

自前でビルドすればほぼどれでもいけるんじゃね?(適当)

【JVMで使いたい】

Kawa一択 かと
SchluesselもJVMで動く

【開発環境が充実してる】

RacketがDr RacketというIDEを持っている
Emacsが使えるならschemeモード使えばいい気がする。
Gaucheならgauche-modeがある。(つかったことないけど便利らしい)

注意事項

Larceny、Mosh、Ypsilonはリリースが年単位で出ていないので、不具合の発見をしても修正される可能性が低め
ChezはCiscoに買収されてから更新が停まっている感じ


他にほしい項目とか、この処理系もお勧めだという突込み歓迎。


追記 2014年1月11日
Larcenyタイポ修正
Schluesselを追加
FFIの項目を多少追加(Sagittariusの宣伝w)

追記 2014年1月13日
Chez Schemeの買収関連のリンクを追加

2014-01-10

ビットフィールドがほしい

実は役に立たないことが判明してしまったMQTTの実装を書いてるのだが、仕様書を読んでいる段階からビットフィールドが(binary data)にあるといいなぁということを思っていた。なんとなく腹案もあって、こんな風に書けるといいかなぁというのが以下、
;; assume define-mqtt-type is defined as composite data
(define-mqtt-type <fixed-header> ()
  ((:bit-field :byte
     (type   :bits 4)
     (dup    :bits 1)
     (qos    :bits 2)
     (retain :bits 1))
   (remaining-length :length)))
うっかりスロット名とかぶるとまずいのでキーワードを使う必要がある(?)。

とりあえず現状ではデフォルトのデータ読み取りなどはないので(多分将来にわたって入れるつもりもない)、:byteが返す値は数値であることをチェックする必要がある。まぁ、しなくてもビット操作したさいに死ぬのでいいといえばいいのだが。いや、読み取った数値が合計ビット数より大きかった場合の処理はいるなぁ。後、読み取るビット数を明示して未使用ビットは暗黙に計算する方がスマートかな?もしくは暗黙に8の倍数を取るか?どっちも一長一短な感じはするなぁ。

この辺を入れだすと、じゃあユニオンもほしい状況が出てくるんじゃないのか?と思っていたりはするのだが、まぁそれは必要になったら考えるとする。

追記
よく考えればsimple datumの定義でなんとかできなくもない気がしてきたなぁ。1バイト読み取ってビットで適当にやればいいのか。無くてもいいかな?ちょっと考えるか・・・

2014-01-02

SRFI-0の紹介

(LISP Library 365参加エントリ)

SRFIとはScheme Requests For Implementationの略。これがライブラリかどうかという議論は置いておくことにする。(8割はPure Schemeで実装可能なので、まぁOKということで。)

一応昇順に紹介していくつもりだが、そうすると最新のものは年の最後の方になるので、適当に「このSRFIが知りたい」とかのコメントなりメンションなり投げてもらえれば適宜対応する予定。

さて、一発目は0番目のSRFI、cond-expandの紹介。

これは何?
処理系毎の差異を吸収するためのマクロです。C言語で言えば#ifdefとかのプリプロセッサみたいなの。

どう使うの?
こんな感じで処理系とか、仕様とかを指定します。
;; want to use record
(cond-expand
  ;; or/and can be used to expand/narrow the condition
  ((or sagittarius r7rs)
    (import (scheme base)))
  (gauche
    (use gauche.record)))
  (else
    ;; define own record
   ))
実はこのSRFIはすでにR7RSに組み込まれています。また、R7RSではR6RS同様モジュールの定義がされたので、cond-expandにlibraryの識別子が入っています。オリジナルのSRFIではsyntax-rules内のキーワードにsrfi-1srfi-5などのサポートしているSRFIを入れていますが、library識別子は処理系が持っているライブラリを探して、存在するかしないかをチェック可能です。

さて、これで処理系の切り分けをしてみたくなりましたね?

2014-01-01

謹賀新年

あけましておめでとうございます。旧年は大変お世話になりました、本年もよろしくお願いいたします。

時候の挨拶ここまで。

新年が始まると浮かれて何かしら決意したりするのが恒例なので今年も何かしら決意しようと思う。

私生活
2012年から言ってるけど、ギターの練習をもう少し真面目にやりたい。弾きたい曲が少ないのがモチベーションを上げれない理由かも。(弾きたいのは難易度高すぎてやる前からしり込みしてるってのもあるが・・・)
昨年末はちょっと忙しくてサボったけど、週1ではジムにいっていたのでこれを継続したい。(平日にいけれないのは痛いが、まぁ家トレで頑張るということで。)
オランダ語をいい加減話せるようにならないと、といい続けて早2年。まだ喋れない・・・今年は頑張る。

Sagittarius
BlackBerryで動くようになってたw
今年はライブラリの充実とMOP周りを頑張ろうかなぁと。まぁ、ここで言っても大抵年末までにはいろいろ予定が変わるので2014年開始2,3ヶ月の予定ということでw
月一リリースでやってるけど、どこまでこれを継続するか悩み中。個人的には小出しにしていけばいろいろ楽だと思ってるんだけど、大掛かりな変更をしづらいというのもあってどうしようかなぁと。
Google Codeから逃げた。ダウンロード機能があるから使っていたのでなくなるなら特に魅力ないし・・・移り先
長期というか、永遠の課題だけど、パフォーマンスの改善とデバッグ機能の強化。特にパフォーマンス。ちょいちょいベンチマークに使ってもらえるようになったけど、結果が散々なのでなんとかしたい。

その他
Lisp Library 365に参加したので、継続的に月3くらいでSRFIの紹介をする。(Sagittariusのライブラリを紹介してほしいという要望があれば応えますw)
R7RSとSRFI縛りで何かを作る過程を綴ってみたいなぁと思いつつ、こいつは未定(ネタ募集) 。

こんなところかなぁ。