Syntax highlighter

2016-03-29

Ellipses expansion of syntax-rules

An interesting post was posted on c.l.s. (c.f. Nested ellipses) It's about how ellipses of syntax-rules should be expanded. The code is as follows:
(define-syntax test
  (syntax-rules ()
    ((test (x ...) ((y ...) ...) )
     '((x (x y) ...) ...) ) ) )
(test (a b c)
      ((1 2 3) (4 5 6) (7 8 9)) )
I'm not sure if this is an error since (x (x y) ...) contains 2 times x followed by an ellipsis. (I think it is, and SRFI-72 expander, a.k.a Van Tonder expander, signals an error.) So I've removed the first x and tested on couple of R6RS and R7RS implementations.
;; Removed the first x
(define-syntax test
  (syntax-rules ()
    ((test (x ...) ((y ...) ...) )
     '(((x y) ...) ...) ) ) )
(test (a b c)
      ((1 2 3) (4 5 6) (7 8 9)) )
#|
Either:
#1
(((a 1) (b 2) (c 3))
 ((a 4) (b 5) (c 6))
 ((a 7) (b 8) (c 9)))
Or
#2
(((a 1) (a 2) (a 3))
 ((b 4) (b 5) (b 6))
 ((c 7) (c 8) (c 9))) 
|#
Implementations emit the #1 are the following:
  • All R6RS implementations
  • Foment
Implementations emit the #2 are the following:
  • Chibi
  • Sagittarius using (scheme base) library
  • Gauche
  • Picrin
To me, if I modify the template like above, it should emit the #1 result. Both R6RS and R7RS have some kind of specification of this pattern:
Pattern variables that occur in subpatterns followed by one or more ellipses may occur only in subtemplates that are followed by (at least) as many ellipses. These pattern variables are replaced in the output by the input subforms to which they are bound, distributed as specified.
R6RS: 11.19 - Macro transformers
Pattern variables that occur in subpatterns followed by one or more instances of the identifier ellipsis are allowed only in subtemplates that are followed by as many instances of ellipsis . They are replaced in the output by all of the elements they match in the input, distributed as indicated.
R7RS: 4.3.2 - Pattern language
I think the difference between R6RS and R7RS is matched ellipses consuming part. R6RS also requires the following:
The subtemplate must contain at least one pattern variable from a subpattern followed by an ellipsis, and for at least one such pattern variable, the subtemplate must be followed by exactly as many ellipses as the subpattern in which the pattern variable appears. (Otherwise, the expander would not be able to determine how many times the subform should be repeated in the output.)

This, I believe, restricts that input expressions of the multiple ellipses have the same length of input. In above example, x and y should have the same length. R7RS, on the other hand, requires to consume all input. Thus, x and y may have different length of inputs (e.g. (test (a b c) ((1 2 3 10) (4 5 6) (7 8 9))) should be valid on above example). In such cases, the expander can't determine how it should be expanded if it needs to expand like R6RS does (as R6RS mentioned).

Maybe there's more direct statement which specifies the behaviour of this case.

2016-03-27

肉体改造部 第十二週

風邪ひいて一週間マルッと寝込んでいたりして二週飛ばし。こんなんばっかだな。。。

計量結果:

  • 体重: 71,2kg (+1.7kg)
  • 体脂肪率: 22.1% (+1.6%)
  • 筋肉率:43.2% (+0.6%)
体重が落ちたのはまず間違いなく風邪のせいだと思われる。まぁ、落とすことも目標ではあるので、問題はないのだが。

なんだかんだで食べ過ぎるなぁという感じがあるので、頑張って食欲に負けないようにしないといけないのだが、「ダイエットは明日から」という言葉を使う人の気持ちが分かるレベルで誘惑に負けそうになる(というか負けてる)。

2016-03-26

ファイルシステムの監視 実装編

とりあえず、inotify、kqueueとReadDirectoryChangesWの3つで大体同じように動くものができた。例えばtailコマンドっぽい何かは以下のように書くことができる。
;; tail.scm
(import (rnrs) (getopt) (sagittarius filewatch) (binary io))

(define (tail file offset)
  (define watcher (make-filesystem-watcher))
  (define in (open-file-input-port file))
  ;; dump contents to stdout
  (define (dump)
    (let loop ()
      (let ((line (get-line in)))
        (unless (eof-object? line) 
          (put-bytevector (standard-output-port) line)
          (put-bytevector (standard-output-port) #vu8(10))
          (loop)))))
  (define size (file-size-in-bytes file))
  ;; move port position if the size if more than offset
  (when (> size offset) (set-port-position! in (- size offset)))
  ;; dump first
  (dump)
  ;; add path to file watcher
  (filesystem-watcher-add-path! watcher file '(modify) 
                                (lambda (path event) (dump)))
  ;; monitor on foreground.
  (filesystem-watcher-start-monitoring! watcher :background #f))

;; this tail is not line oriented
;; it shows tail of the file from the given offset.
(define (main args)
  (with-args (cdr args)
      ((offset (#\o "offset") #t "1024")
       . rest)
    (tail (car rest) (string->number offset))))
#|
sash tail.scm foo
|#
これで延々とファイルに追加されたものを標準出力に吐き出していく。まだドキュメント化していないが、捻りを加える必要もないだろうし、多分これが最終形になると思われる。

実装に関して
前回も書いたが三者三様なのでそれぞれ苦労した。inotifyは素直にできているのでLinuxのinotify(7)にあるサンプルを参考にしながらで十分だった。思ったとおりこれが一番楽だった。次いでkqueueなのだが、こいつは例があまりなかったのと、kqueue自体が非常に総称的にできているので理解するまでに苦労した。理解してしまえばまぁそれほどという感じではある。ReadDirectoryChangesWはこれ自体はそんなに複雑じゃないんだけど、どちらかというとそれ以外の部分(OVERLAPPEDとかFILE_NOTIFY_INFORMATIONとか)が多少面倒だった感じ。動いてるけど正しく実装したのか自信ない。

実装間に於ける制限
意外だったのはkqueueが制限が一番大きくなったこと。kqueueはファイルの監視はできるけど、ディレクトリの監視をした際にどのファイルが変更されたとか追加されたとかを知る術がない。ものすごく頑張ればやれなくないんだけど、監視対象毎にファイルディスクリプタが必要になるので、万を超えるファイルとかがあるディレクトリの監視とかすると普通に死にそう。(ものすごく頑張る必要性を今のところ感じていないので頑張っていないが・・・)
次いでinotify。ディレクトリの再帰的監視は頑張らないと無理(kqueueも無理だけど)。まぁ、再帰的に監視したいかと言われるとよく分からないが。とりあえず他の実装もこれにあわせるようにしてお茶を濁した。
Windowsはファイルの監視ができないんだけど、ディレクトリの監視をすればどのファイルが変更されたかの情報が取れるので特に問題なかった。ありそうなのは、Windows Vista以降ではデフォルトでアクセス時間の変更がされないので、それの監視をしたい場合はシステムを弄らないといけないことか(実装とは関係ない) 。

落とし穴
Cygwinが実は落とし穴だった。CygwinはPOSIX環境を提供してくれるんだけど、inotifyもkqueueもPOSIXじゃないので存在しない。そしてCygwin自体にファイル等を監視するようなAPIもない。どうしたかといえば、Windowsの実装の上にパス変換(cygwin_conv_path)を噛ませるようにした。また、監視を止めるのに他の環境だとスレッドに割り込みをかけるようにしているが、Windowsのコードを流用しなければならないのでそれができない(Cygwinはスレッドの割り込みにシグナルを使うがWindowsはSetEventを使っている)。しょうがないので泥臭い方法で回避している。

所感
疲れた。後は使いつついじっていく感じかな。

2016-03-22

ファイルシステムの監視

最近ファイルの変更を検知したいという要望が僕の中であがってきている。追記型のログファイルを調べるとかそんなちょっとしたことからなんだけど、あると便利かなぁと思い始めてきた。っで、いろいろ調べてみた結果OS毎に作法が全然違うという悲しい事実が判明。Linuxはinotify、WindowsはReadDirectoryChangesW、*BSDはkqueue、OSXはFSEvents(だけど、kqueueも使えるっぽいのでそっち使う予定。手元にOSないし)みたいである。

ちらっと調べてみた感じでは、それぞれに一長一短あるんだけど、個人的にはinotifyが一番楽っぽいイメージ。次いでkqueue。Windowsはやりたいことはやれなくないけど結構大変っぽい。Windowsの問題はファイルシステムの監視がフォルダのみというところで、ファイル自体の変更を検知する直接的な方法はないっぽい。パスを分解、フォルダを監視、その上でターゲットのファイルが変更されたかどうかをチェックするという方法になりそう。

一番楽っぽいかなと思われるLinuxの実装は既にリポジトリに入れた。どのイベントを取るかとか、ディレクトリが指定された際はどうするかとかまだ考えないといけないけど、単純なファイルの監視というところは動いている。次はWindows+Cygwinを何とかしつつ、kqueueは最後にやる感じ。一番問題になるのは、動作をそろえるところだろうなぁ。こればっかりは地道にやるしかないので、ある程度作ったら使いながら調節するという感じになるような気がする。

2016-03-19

ズンドコキヨシ

風邪(と思われる)で1週間寝込んでいたのだが、体調がほぼ戻ってきた。Twitter等でズンドコキヨシなるものを見かけたので、1週間ぶりにリハビリを兼ねて書いてみた。(1週間もコード書かないと鈍るよね?)

#!r6rs
(import (rnrs) (srfi :27))

(define zun "ズン")
(define doko "ドコ")
(define kiyoshi "キ・ヨ・シ!")

(define (zun? o) (string=? zun o))
(define (doko? o) (string=? doko o))
(define (kiyoshi! o) (display o) (display kiyoshi) (exit 0))

(define zun&doko (vector zun doko))
(define (zundoko-generator) (vector-ref zun&doko (random-integer 2)))

(define init-state 0)
(define (gen-next n) (lambda (o) (display o) n))
(define ->init (gen-next init-state))
(define states
  `#((,zun?  ,(gen-next 1) ,->init)
     (,zun?  ,(gen-next 2) ,->init)
     (,zun?  ,(gen-next 3) ,->init)
     (,zun?  ,(gen-next 4) ,->init)
     (,doko? ,kiyoshi! ,(gen-next 4)) ;; more than 4 zun, loop it
     ))

(random-source-randomize! default-random-source)

(let loop ((ns init-state))
  (let ((token (zundoko-generator))
        (state (vector-ref states ns)))
    (if ((car state) token)
        (loop ((cadr state) token))
        (loop ((caddr state) token)))))
普通に4回と1回を数えた方がすっきりするような気もしないでもない。

2016-03-07

オランダの転職エージェント

Amazonのオファーを蹴ってしまった+収入を増やしたい(いろいろ要りようなのですよ)という思いから転職活動をしている。なんだかんだ(いろいろリスクはあるが)で手っ取り早く収入を増やすならよりよい条件の職場に行くのが早い。そうはいっても、あまりがつがつ転職活動をするというつもりもなく、いい条件の職があったらという感じでゆる~くやっているのではあるが。

オランダに限らず転職は3種類くらいパターンがあると思う。
  • 自分で探す
  • 向こうから声をかけられる
  • エージェント経由
二つ目と三つ目は同じかもしれないが、あえて分けている。正直エージェントを使うのは好きではないのだが(理由は後述する)、今回は会社がLeidenにあるということでなんとなく面接することにした。オランダで転職エージェント経由で転職活動するのは大体以下のような流れになる。
  1. エージェントから連絡がくる
  2. 先方との面接日時を決める
    1. 面接
  3. エージェント経由でフィードバックを受け取る
  4. 採用、不採用が決まるまで2-3までを繰り返す
2-3のループは大体2回、多くても3回という感じ。その合間辺りで希望する年収(もしくは月収)を聞かれる。

さて、これだけならわざわざブログの記事にすることもないのだが、ちょっと頭にきたことがあったりして吐き出しを兼ねて適当にエージェントを使うのが嫌いな理由を書いていく。

【転職理由】
大抵のエージェントは、なんでこの会社にいきたいか、みたいな事を質問してくる。ついでになんで転職するのかとか。一度素直に「金」と答えたら、「それじゃだめだ」みたいなことを言われたことがある。仕事内容なんてお前らが言ってることと一致したことねえんだよ!転職理由なんて金払いがよければそれで十分だ!僕にとって「やりがい」とかは副次的であって、主目的は「金」だよ!「やりがい」で腹は膨れないっつーの!

【希望収入】
少なくともオランダでこれを聞かれるときには、セットで現在の収入も答える必要がある。ここで、現在の収入を真面目に答えると損をするので必ず月収なら500ユーロは多く答えておくとよい(経験談)。
転職するのであれば、よほど今の会社から逃げたいとかを除いて、収入が上がることを期待したいものである。よくも悪くも転職にはリスクが伴うし(ペンションとか、職歴とか)。っで、現在の収入より月500ユーロ多くもらえるのを希望すると、「500ユーロも増やせると思うの?」とか「現在の収入からみて妥当なところを探す」とか言われることがある(3分の2のの確立)。ぶっちゃけ、これを言われたら萎える(萎えた、今日)。Nettで300ユーロ増やすのがそんなに悪ですか?あの手この手使って最低ラインを下げようとしてますよね?ぶっちゃっけ月100ユーロ増える程度では職変えないぜ、普通。お前らの交渉能力の低さを棚に上げてこっちにばかり妥協点を押し付けんな!あんまり声を荒げるとか、大人気なく喧嘩する気もないので、希望額に届かなかったら容赦なく辞退するだけですよ。

【自己矛盾】
今の会社もエージェント経由だったんだけど、どうも同じ会社っぽいんだよね。当時(一年前だが)の担当(今の担当の上司らしい)は、この会社に採用された人は長く続けてるからこの会社はいい会社だ、みたいなこと言ってたんだけどねぇ。まぁ、人売り人買いの会社なんて早々に転職させて金儲けしてるんだから当然なのかもしれないけど、この節操のなさにはこっちもびっくりですよ。

【恩着せがましい】
「この会社に他の人を送るのストップしてる」というのは彼らの殺し文句である。そっちの事情は知ったことではないのだよ。それで恩を売って、決まった際に「これだけやったんだから給料が低くても転職しろ」みたいな態度に出られてはたまらない。お前らは仕事、こっちはリスクを負う、恩も義理もない。ビジネスでやってるのに、人情を人質に取ろうとするのに反吐がでる。多少の害には目をつぶれってか?ふざけんな!

適当に書きなぐってしまった。使えそうなら使うくらいの立場でいた方がいいということ。変に義理立てしたり、向こうの意味不明な言論に左右されないというのが大事である。あぁ、腹立った。

2016-03-06

肉体改造部 第九週

なんかいろいろあって2週ほど飛ばしてしまった。

計量結果:

  • 体重: 72.9kg (+0.1kg)
  • 体脂肪率: 23.7% (+0.1%)
  • 筋肉率:42.6% (±0.0%)
先々週のことはあんまり覚えていないんだけど、先週は油断してちと食べ過ぎたのとあんまり筋トレに時間が割けなかった(筋トレの消費カロリーがどれくらいかは知らないけど…)ことを考えると順当に増えたといえるか?

最近懸垂が普通に10回x4セットくらいできるようになってきたのでちょいちょい筋肉が付いてきたのではと思っているのだが、数字には表れていない様子(当てになるかもよく分からんけど)。懸垂しても上腕二頭筋にあまり負荷がかかっていない感じがするということは、自重トレーニングする分には十分ということなのだろうか?ジムに行きたいところではあるが、時間が取れないんだよなぁ。

2016-03-04

Cache

I'm currently working on ORM library (this) and have figured out that creating prepared statement is more expensive than I expected. You might be curious  how much more expensive? Here is the simple benchmark script I've used.
(import (rnrs)
        (time)
        (sagittarius control)
        (postgresql))

(define conn (make-postgresql-connection
              "localhost" "5432" #f "postgres" "postgres"))
;; prepare the environment
(postgresql-open-connection! conn)
(postgresql-login! conn)
(guard (e (else #t)) (postgresql-execute-sql! conn "drop table test"))
(guard (e (else #t))
  (postgresql-execute-sql! conn "create table test (data bytea)"))

(postgresql-terminate! conn)

;; let's do some benchmark
(postgresql-open-connection! conn)
(postgresql-login! conn)

(define data
  (call-with-input-file "bench.scm" get-bytevector-all :transcoder #f))

(define (insert-it p)
  (postgresql-bind-parameters! p data)
  (postgresql-execute! p))

;; Re-using prepared statement
(let ((p (postgresql-prepared-statement
          conn "insert into test (data) values ($1)")))
  (time (dotimes (i 10) (insert-it p)))
  (postgresql-close-prepared-statement! p))

(define (create-it)
  (let ((p (postgresql-prepared-statement
            conn "insert into test (data) values ($1)")))
    (insert-it p)
    (postgresql-close-prepared-statement! p)))
;; Creating prepared statement each time
(time (dotimes (i 10) (create-it)))

;; bye bye
(postgresql-terminate! conn)
I'm using (postgresql) library.  <ad>BTW, I think this is the only portable library that can access database. So you gotta check it out. </ad> It's simply inserting the same binary data (in this case the script file itself) 10 times. One is re-using prepared statement, the other one is creating it each time. The difference is the following:
$ sash bench.scm

;;  (dotimes (i 10) (insert-it p))
;;  0.760319 real    0.008487 user    3.34e-40 sys

;;  (dotimes (i 10) (create-it))
;;  1.597769 real    0.014841 user    5.76e-40 sys
More than double. It's just doing 10 iterations but this much difference. (Please ignore the fact that the library itself is already slow.) There are probably couple of reasons including PostgreSQL itself but from the library perspective, sending messages to the server would be slow. Wherever a DB server is, even localhost, communication between script and the server is done via socket. And calling postgresql-prepared-statement does at least 7 times of I/O (and 5 times for postgresql-close-prepared-statement!). So if I re-use it, then in total 120 times (12 x 10, of course) of I/O can be saved.

Now, my ORM library hides low level operations such as creating prepared statement, connection management, etc. (that's what ORM should do, isn't it?). So keeping prepared statement in users' script isn't an option. Especially, there's no guarantee that users woudl get the same connection each time they do some operation. So it's better to manage it on the framework.

Sagittarius has (cache lru) library, undocumented though, so first I thought I could use this. After modifying couple of lines and found out, no this isn't enough. The library only provides very simple cache mechanism. It even doesn't provide a way to get all objects inside the cache. It's okay if the object doesn't need any resource management, however prepared statements must be closed when it's no longer used. Plus, LRU may not be good enough for all situations so it might be better if users can specify which cache algorithm should be used.

There are variety of cache algorithms. Implementing all of them would take a bit time. So it's better to make a framework or interface of cache. The framework/interface should have the following properties:
  • Auto eviction and evict event handler
  • Limitation of storage size (unlimited as well)
  • A way to get all cached objects
  • Implementation independent interface
Now, make myself busy.