Syntax highlighter

2017-03-10

JSONユーティリティ

最近の動向として猫も杓子もJSONとなっている感じがあるが、SagittariusではJSONのサポートが薄い(Githubに拙作のJSON Toolsを置いているが本体に入れようかなぁ)ので多少手厚くしようかなぁと思いSchemeオブジェクトに変換するライブラリを書いたりしてみた。まぁ、将来的にJWTとか実装しようと思うと生のデータは扱いづらいがDSLクエリーでは効率が悪いという話もあった。

以下のように使える
(import (rnrs) (text json object-builder))

;; JSON string
(define json-string "{
  \"Image\": {
    \"Width\":  800,
    \"Height\": 600,
    \"Title\":  \"View from 15th Floor\",
    \"Thumbnail\": {
      \"Url\":    \"http://www.example.com/image/481989943\",
      \"Height\": 125,
      \"Width\":  100
  },
    \"Animated\" : false,
    \"IDs\": [116, 943, 234, 38793]
  }
}")

;; records represent JSON object
(define-record-type image-holder
  (fields image))
(define-record-type image
  (fields width height title thumbnail animated ids))
(define-record-type thumbnail
  (fields url height width))

;; JSON -> Scheme object definition
(define builder (json-object-builder
                 (make-image-holder
                  ("Image"
                   (make-image
                    "Width"
                    "Height"
                    "Title"
                    ("Thumbnail"
                     (make-thumbnail
                      "Url"
                      "Height"
                      "Width"))
                    "Animated"
                    ("IDs" (@ list)))))))

;; Scheme object -> JSON definition
(define serializer (json-object-serializer
                    (("Image" image-holder-image
                      (("Width" image-width)
                       ("Height" image-height)
                       ("Title" image-title)
                       ("Thumbnail" image-thumbnail
                        (("Url" thumbnail-url)
                         ("Height" thumbnail-height)
                         ("Width" thumbnail-width)))
                       ("Animated" image-animated)
                       ("IDs" image-ids (->)))))))

(let ((image (json-string->object json-string builder)))
  ;; do some useful thing with image

  ;; ok I want JSON string
  (object->json-string image serializer))
;; Formatted for convenience
;; -> {
;;      "Image": {
;;        "Width": 800,
;;        "Height": 600,
;;        "Title": "View from 15th Floor",
;;        "Thumbnail": {
;;          "Url": "http://www.example.com/image/481989943",
;;          "Height": 125, "Width": 100
;;        },
;;        "Animated": false,
;;        "IDs": [116, 943, 234, 38793]
;;      }
;;    }
リストとかベクタとかも扱えたりするので、まぁ要りそうな機能はざっくりあるかなぁという感じ。

CLOSを使ってJavaのアノテーションよろしくゴテゴテした感じにしてもよかったんだけど、最近そういう書き方を避けてるのと、既存のレコード等も再利用可能にするためにこんな感じのデザイン。SXMLに対してのオブジェクト構築と似た感じになってるのはそっちもそんな感じのデザインで作ってあるから。

使い勝手は(当面のJSONが必要な場面がこれなので)JWTを実装しつつ見ていくことになるが、そこまで大きな変更はないだろうなぁと思ったり。

2017-03-02

Hibernateでクエリー爆発した話

JavaでORMと言えば真っ先に思い浮かぶのがHibernateであろう。データベースをほぼJava Beansのように扱える便利なライブラリである。ともすれば、裏で何が起きているのか全く感知しなくてもいいので、SQLが嫌いな人にはなかなかに受けが良いようである(要出典)

さて、裏で何が起きているのかを意識しなくていいというのは多くの場合便利な反面問題が起きた際の検知または解決が遅れるということでもある。データベースアクセスというのは大体の場合物理ファイルへのアクセスがあり、クエリの実行にはソケットが使われるので通信が発生する。これらはパフォーマンスを大きく損なう操作でもあり、一般的(誰?)は可能な限り避けるべきとされている(要出典)。ここではHibernateによって隠されたこれらの操作が引き起こすパフォーマンス劣化の体験談を今後の戒めとして記録しておく。

問題になったのはだいたいこんな感じのテーブル。
                       +--------------+
   +--------+        |  X_User_Dep  |    +-------------+          +-----------+
   |  User  |          +--------------+           |  X_UD_Prod  |          |  Product  |
   +--------+ 1   1..* |  + ID        | 1    1..* +-------------+          +-----------+
   | + ID   | o------o |  + User_ID   | o-------o |  + UD_ID    | 1..*   1 |  + ID     |
   | + Name |          |  + Dep_ID    |         |  + Prod_ID  | o------o |  + Name   |
   +--------+        +--------------+    +-------------+          +-----------+
                        o 1..*
           |
           |
                 o 1
                       +--------------+
                       |  Department  |
                       +--------------+
                       |  + ID        |
                       |  + Name      |
                       +--------------+
Java側はこんな感じのエンティティ
@Entity
@Table(name = "User")
public class User {
    @Id
    private int id;
    @Column(name="Name")
    private String name;
    @OneToMany(mappedBy = "user")
    private Set<UserDepartment> userDepartments;
}

@Entity
@Table(name = "Department")
public class Department {
    @Id
    private int id;
    @Column(name="Name")
    private String name;
    @OneToMany(mappedBy = "department")
    private Set<UserDepartment> userDepartments;
}

@Entity
@Table(name = "Product")
public class Procdut {
    @Id
    private int id;
    @Column(name="Name")
    private String name;
}

@Entity
@Table(name = "X_User_Dep")
public class UserDepartment {
    @Id
    private int id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "User_ID")
    private User user;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "Dep_ID")
    private Department department;
    @OneToMany(mappedBy = "product")
    private Set<Product> products;
}

@Entity
@Table(name = "X_UD_Prod")
public class UserProduct {
    @Id
    private int id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "UD_ID")
    private UserDepartment;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "Prod_ID")
    private Product product;
}
ビジネスロジックは以下のようなことを実行する:
  • ユーザAが所属する部署に所属する全てのユーザが保持するプロダクトを得る
  • 適切なオブジェクトに変換し返す
普通に考えれば、まず条件を満たす全てのレコードを取得し変換処理をするだろう。僕が目にしたコードは以下のようなことをしていた。
  • ユーザAの所属する部署を得る
  • 上記の部署に所属する全てのユーザを得る
  • 上記で得られたユーザ毎にプロダクトを取得し、変換する
SQLが直接見えるのであればこの処理がO(n*m)かかるひどいものだとすぐに気づくのだが、Javaのコードを見ると大体以下のように実装されていた。

getAllUserInDepartment(thisUser.getDepartment()).stream()
    .map(UserDepartment::getProducts)
    .map(convert)
    .collect(Collectors.toList);
これの困ったところは、最初のgetAllUserInDepartment以外は目に見えたデータベースアクセスがないということ。実際にはproductsはLAZYに初期化されているので、convertないで他の何らかProductのプロパティにアクセスして初めて取得のクエリが走る。EAGERに取得しろよという話もあるが、このオブジェクトが使われているのはここだけではないので影響範囲に責任持ちたくないという無責任さからやめた。キャッシュという選択肢もあったんだけど、使われているデータベースへのアクセスがJavaアプリケーション以外からもあるので多分キャッシュの整合性が取れないということ(+僕自身Hibernateにそこまで精通していないの)で断念。

どうしたかといえば、エンティティ定義は変えずに、ひたすら下請けのSQLがどんな風になるかを想像しながらDetachedCriteriaを書いて逃げた。正直、SQL直接書かせてください、お願いしますという気分にはなったが…こういう(キャッシュ使えない、エンティティ定義変更できない)時はどうするのがベストプラクティスなのか興味があるが、僕のググり力の低さのせいで解決方法は見つからず。

Hibernate便利なんだけどある程度SQLが書けると隠しすぎてて辛いという時があるという話。

2017-02-09

楕円曲線暗号

今年の抱負の一つ、楕円曲線暗号を実装している。とりあえず肝の一つであるECDSAを実装し終えた。こんな感じで使える。
(import (rnrs) (crypto))

(define keypair (generate-key-pair ECDSA :ec-parameter NIST-P-521))

(define msg "this message requires digital signature")

(define ec-signer (make-cipher ECDSA (keypair-private keypair)))

(define signature (cipher-signature ec-signer (string->utf8 msg)))

(define ec-verifier (make-cipher ECDSA (keypair-public keypair)))

(cipher-verify ec-verifier (string->utf8 msg) signature)
;; -> #t
一応NISTが推奨する楕円曲線パラメータは全部実装してある。テストベクタもあるので、当然テストもしてある。おかげでテスト時間が増大した。処理が重い…SECGが推奨するパラメタも実装しないとなぁと思いつつ割と現状で満足している感もあり、既に随時追加する方向にシフトしつつある。

今となっては笑い話だが、実装している間で一番苦労したのが「正式な仕様を見つけること」だったりする。暗号系に限らず、プロトコルの実装をする際は可能な限り「正式な仕様」(RFCとか)を当たるようにしているのだが、楕円曲線暗号はこれを見つけるのに苦労した。Google先生にお伺いを立てると大抵RFCに当たるんだけど、探した限りだとRFCにはECDSAの拡張はあってもそれ自体がない。まさかWikipediaに書いてあるのをそのまま(実際正しかったんだけど)鵜呑みにするのもなぁと思い探したら、行き着いた先はANSIだったという。最終的にはX9.62でググるのが正解というのに辿り着いた。こういうの知らんと探せんなぁと思った次第。

実はこれを実装してようやくスタートラインに立ったというのが正直なところ。ここからECDH、SSHとTLSにECDSA+ECDHを追加していく予定。まだ先は長いが、今年の抱負なので、今年中に終わればいいだろうくらいな気持ちでいたりはする。

2017-02-01

ひらけ!ポンキッキ

r/lisp_jaとTwitterに以下の投稿があった。
前者がGuileで後者はR6RSで実装されている。文字列をグルグルさせるのならSRFI-13のxsubstringが使えるだろう思い、僕も書いてみた。
(import (rnrs) (srfi :13))

(define s "ひらけ!ポンキッキ")

(define-syntax do-while
  (syntax-rules ()
    ((_ ((var init ...) ...) (pred r) commands ...)
     (do ((first #t #f) (var init ...) ...)
         ((and (not first) pred) r)
       commands ...))))

(do-while ((t s (xsubstring t 1))) ((string=? s t) #t) (display t) (newline))
Cのdo ... whileを真似たdo-whileマクロは正直いらんけど…
以下は実行結果
$ sash hirake.scm
ひらけ!ポンキッキ
らけ!ポンキッキひ
け!ポンキッキひら
!ポンキッキひらけ
ポンキッキひらけ!
ンキッキひらけ!ポ
キッキひらけ!ポン
ッキひらけ!ポンキ
キひらけ!ポンキッ
毎回文字列の比較をするので、当然効率は良くないが、まぁこういうこともできるということで。

2017-01-31

一移民として

オランダに移住してそろそろ8年になる。移民の定義的(Wikipediaが正しいかは知らんが)には移民で問題ないだろう。

大統領令

さて、一移民として最近ニュースサイトから目が離せないでいる。もちろん今を賑わす時の人、第45代アメリカ大統領ドナルド・トランプ氏関係のニュースである。先日あった特定7カ国籍を持つ人のアメリカ入国拒否には大きな衝撃を与えられた。
なぜこのニュースが大きな衝撃になるのか疑問に思うかもしれない。ニュースサイトの論調はともかくとして、傍目から見ればテロリストの抑止にも見えなくない(それにしても、ビザを持っているのに入国できないという事実があったことには目を瞑る必要はあるのだが…)。一移民の視点、少なくとも僕個人の視点、から見ればこれは移民排斥の第一歩に写った。

トランプ氏は就任式の際に「アメリカ第一」と唱えている。
それと同時に大統領選挙の際のスピーチにいくつか人種差別的な発言もある。
これらのニュースから導き出された僕個人が描くトランプ像は白人アメリカ主義者というものになっている。

この「アメリカ第一」と個人的なトランプ像から、先日の大統領令はある種の試金石的な位置づけにあるのではないかと思っている。つまり、テロリスト排除という錦の御旗を掲げることでどれくらい潜在的な人種または宗教差別を隠せるか。そこから発展させて最終的には白人主義に持っていく道筋を建てようとしたのではないか。

ここまで来ると偏執病ではないかと思わなくもないが、過去にナチが存在したという事実もある。あまり考えたくないが歴史は繰り返されるものであるのであれば、ナチズムが再び起きることもありえなくない。

対岸の火事?

事件はアメリカで起きているのだ、オランダに住む僕にはあまり関係ないのではないか?と思わなくもない。っが、意外にも周りのオランダ人(国籍的に、人種的には違う)的にはトランプの大統領令を歓迎する人もいる。難民に関して言えば、ヨーロッパも難民問題に悩まされている。隣国のドイツからは難民関係の事件が絶え間なく流れてくる。
また、割と多数(少なくとも僕の周り)のオランダ人は「中東からくるイスラム系移民は全てテロリスト」と誇張を含むとはいえそれなりに真剣に言っている。

中東移民全テロリスト発言も井戸端会議で話されている程度であればまだ可愛げもあるかもしれない。しかし、それを掲げる政治家がいるとなれば話は多少違ってくる。

Geert Wildersはオランダの反イスラム主義政治家である。現状のところ反イスラム主義だけではあるが、いつ移民排斥になるかは検討がつかない。実際、彼はヨーロッパの難民受け入れ体制に反対している。(もっとも難民の多くはイスラム系なので難民の受け入れに反対なのかは判断が難しいところではあるが…)
また、彼が率いるPVV(Partij Voor de Vrijheid、訳:自由党)は2009年に議席数を150議席中32議席と大きく伸ばしている。(この年はちょうどオランダに来た年で、極右の政党が大幅に議席数を伸ばしたこのニュースはオランダに住むことを不安にさせられた。) 議席を伸ばしたということは、少なくない数のオランダ人が彼の政策に賛同しているということである。それが、反イスラム主義なのか、別の政策に大してなのかはわからないが、賛同者にとって反イスラム主義は問題にならないとも言える。

まとめ

自国を離れていると、こういうニュースには常に戦慄させられる。いつか自分がその対象になる可能性があるからだ。トランプ大統領政権下で何が起きるのか見てみたいという好奇心と、とっとと彼を引きずり下ろして心に平穏を与えてほしいという相反する2つの感情がせめぎ合っている気がする。

2017-01-22

R7RS-largeサポート

本当は日本語リリースノート的なのにするつもりだったのだが、あまりにも眠くてリリース作業だけして寝てしまったという…

Sagittariusは0.8.0からR7RS-largeのRedEditionをサポートするようになった。具体的には以下のライブラリが使用可能になる。
  • (scheme list) - SRFI-1のエイリアス
  • (scheme vector) - SRFI-133のエイリアス
  • (scheme sort) - SRFI-132のエイリアス
  • (scheme set) - SRFI-113のエイリアス
  • (scheme charset) -  SRFI-14のエイリアス 
  • (scheme hash-table) - SRFI-125のエイリアス
  • (scheme ilist) - SRFI-116のエイリアス
  • (scheme rlist) -  SRFI-101の手続きをリネーム
  • (scheme ideque) - SRFI-134のエイリアス 
  • (scheme text) -  SRFI-135のエイリアス
  • (scheme generator) -  SRFI-121のエイリアス 
  • (scheme lseq) - SRFI-127のエイリアス
  • (scheme stream) -  SRFI-41のエイリアス
  • (scheme box) -  SRFI-111のエイリアス 
  • (scheme list-queue) - SRFI-117のエイリアス
  • (scheme ephemeron) -  SRFI-124のエイリアス
  • (scheme comparator) -  SRFI-128のエイリアス
ライブラリ名等がR7RSのWikiにこそっと修正されて上がっていた。個人的にこういうのはどこかに投げてほしい、c.l.sとか。でないとマジで見落とす…

注意するところとして、
  • (scheme ilist)で作られる不変リストは通常のcar等では扱えない
  • (scheme rlist)で作られるランダムリストは、同上
  • (scheme ephemeron)は厳密にはephemeronではない(が仕様は満たしている)
くらいだろうか。

RedEditionだと単に便利ライブラリが標準に追加されたくらいの感覚しかないが、それでもR7RS-largeをフルサポートしている処理系は現状ではSagittariusだけではないだろうか。

そういえば、その昔R7RS-largeに(scheme inquery)というライブラリが追加されたことがあるのだが、これって正式に仕様になってるのかな?SRFI-112とかWG2とか見ても見つからないから、ライブラリ名は先走りだったかな?

2017-01-02

謹賀新年

年末に2016年のことを振り返る余裕がなかったので、新年の豊富とともに振り返ってみんとする。

2016年を振り返る
個人的にいろいろ激動な感があった(感があっただけで激動はしていないが) 。Amazonのオファーをキャンセルしたり、転職したり。Scheme Workshop 2016にも行ったなぁ。10月以降は現職での仕事が忙しかったのであまり何もないけど、Schemeで書いたサービス(と呼べるほどでもないが)を立ち上げたりしたか。

忙しいのと反比例してSagittariusの開発は少し停滞していた感がある。 適当にSRFIを追加した以外は特に目新しい機能もなく、といった感じである。忙しくても身は一つなので割ける時間は減るといった当たり前のことを痛感した年でもあった。

筋トレをすると言ったのだが、蓋を開けてみると志半ばで挫折した感がある。年の後半は特に時間が割けなかった。

2017年の豊富
酉年だから飛躍の年、とか言うつもりはないが、以下のことを通年でしようかなぁと
  • 筋トレ。月水金くらいで30分以上くらい自重トレ。行けたらジムにも行く。
  • ギターの練習。週末くらいで
  • 17時退勤。仕事は二の次くらいの勢いで
  • 本を読む。月一冊くらいは読みたいところ
Sagittarius的には以下をなんとかしたいと思っている
  • OAuth
  • 楕円曲線暗号(RFC6090)
  • R7RS-large対応
  • 日本語ドキュメント
最初の2つは当面という感じだが、下2つはちょいちょいやっていく感じかな。どうせ何かしら見つけて作っていくとは思うけど。

基本的には、なんとかなる、ゆるーくまったり、くらいの気持ちでいこうかなぁと思っている。ということで、今年もよろしくお願いしますm(_ _)m

2016-12-27

Anxiety

I thought I've written exactly the same blog post last year, but I guess I didn't. Maybe I was smart enough not to show this negative feeling.

It's almost the end of year. Every year, I've been thinking the same thing in this season and anxious. That is my skill, in one word. It might also be called my career path, knowledge or experience. I just don't know how I can describe what I'm worrying for.

Since 2004, I've been working as a software developer. There are couple of years of blank periods but most of my career is being Java developer, good or bad. When I started my career, I was just an ordinary newbie who had a bit of programming skill and assigned to the *deadline is already decided* project. Everything was new including somewhat deadline was there but no specification situation, and there were lots of things to learn, like how to capture screen shot and put it into excel sheet... At that moment, struts 1.0 was there and the concept of DI has just come. After 2 years, I quit the first job and became server administrator... sort of.

In year 2009, I've moved to The Netherlands. At that time, I had some blank as a Java developer. So I didn't know when CI is appeared. DI was still there but a bit brushed up. I could use Java annotation instead of writing huge application-context.xml. I still had lots of things to learn, however I just noticed that I was just learning the usage of libraries. If those libraries disappeared in 5 years, then this knowledge is just useless. Luckily, I've met Scheme and started writing own implementation to build some tools I can use on daily routine.

Writing a Scheme implementation gave me lots of challenges. I wasn't a good C programmer, well am still not, though. How function pointer works, how x86 works, how to retrieve stack area information, difference between POSIX and Windows, those were all new for me. I've never read C89 or C99 specification, so I will never be a C expert, but at least now I know where to look at if I need to find something.

If you write an implementation, then you also need to write libraries. Fortunately, or unfortunately, there's not much Scheme code. So I needed to write loads of code. If I see the code written in very first period of my Scheme life, those look ugly. I can't say I understand how Scheme works perfectly and I can write universal beautiful code. All what I know is how to write workable code and at least that works for me. Maybe I can say I also now the basic concept of paradigms which are used in Scheme programming, such as first class object and CPS.

7 years later, I'm still anxious. Maybe I feel like I know nothing essential. I know numbers of libraries and how those works. I know how to implement TLS or other network protocols. But are these essential? I only have vague idea what's essential knowledge is. It should be knowledge that I can use; even if I need to make software with a language I've never used; in the different IT field; after 20 years so on. And maybe I've already known that there's no such thing. And maybe the fact that I knew this reality makes me more anxious.

If I become a high skilled programmer, would this be gone? It might be better not to think about this and just study hard since I know I need to know more.

2016-12-25

OAuth

ちょっとしたメモ。

SagittariusはCommon Lispから移植したOAuth1.0を扱うライブラリを持っている。っが、このライブラリ将来OAuth2に対応させれるようにしたのかキーワード引数が大量にあり今一使い勝手が悪いい。っで、最近職場でOAuth2上に構築されたプロトコルの一つであるOpenID Connectを使うということが持ち上がっていることもあり、OAuth2のライブラリがいるかなぁという機運が高まってきている。

割と周知の事実だと思うが、OAuth1.0とOAuth2は互換性がない。RFC5849で定義されているOAuth1.0はRFC6749で定義されているOAuth2によって廃止されているのではあるが、名前の似た二つのプロトコルとしてみた方がよい。事実、RFC6749には以下の段落がある:
The OAuth 1.0 protocol ([RFC5849]), published as an informational document, was the result of a small ad hoc community effort. This Standards Track specification builds on the OAuth 1.0 deployment experience, as well as additional use cases and extensibility requirements gathered from the wider IETF community. The OAuth 2.0 protocol is not backward compatible with OAuth 1.0. The two versions may co-exist on the network, and implementations may choose to support both. However, it is the intention of this specification that new implementations support OAuth 2.0 as specified in this document and that OAuth 1.0 is used only to support existing deployments. The OAuth 2.0 protocol shares very few implementation details with the OAuth 1.0 protocol. Implementers familiar with OAuth 1.0 should approach this document without any assumptions as to its structure and details.
太字にした一文があるため、廃止されているとはいえ両方サポートした方がよさげな感じがしている。例えばWikipediaのList of OAuth providersのページによれば、OAuth1しかサポートしていないプロバイダーも結構ある(Bitbucket、Flickr、Tumbler辺りは個人的に使うかもしれない)。

どちらのプロトコルもクライアントを実装するだけならそんなに難しくないと思っていて、問題になるのはAPIの設計だと思っている。中身を見れば全くの別物なのだが、名前や歴史的経緯を見ると両社は密接なつながりがあるように見える。そうすると、ユーザー(クライアント)としてはシームレスに使えた方がいいかなぁとか考えてしまうわけだ。最終的にアクセストークンを使って保護されたリソースにアクセスするわけだし。そうするとこんな感じにライブラリを構成するのがいいだろうか?
  • (rfc oauth)
    • (rfc oauth v1.0)
      • client and so?
    • (rfc oauth v2.0)
      • client and so?
もしくは、無難に分けるか
  • (rfc oauth1)
  • (rfc oauth2)
無難に分けた方がいい気がするなぁ、変に統合すると混乱しそうだし。現在ある(net oauth)ライブラリはそのままにするが、deprecatedという形にすればいいかな。うまいことやれるなら新しいライブラリを下請けにする形にしたいが、そこまで手が回るかは疑問。

2016-12-19

トップレベルの継続

この記事はLisp Advent Calendar 2016の19日目の記事です。

最近c.l.s.に面白い投稿があった。これである。 要約をすると、R7RSに以下の一文を追加しようという話になる。
It is an error to invoke the continuation of a top-level expression or the expression of a top-level definition more than once.
[訳]トップレベルの式もしくは定義の継続を二度以上起動することはエラーである。
この一文を入れる根拠が以下のコードになる(行の関係上短縮記述を使う)。
(define-library (foo)
  (import (scheme base))
  (export reload count)
  (begin
    (define counter (vector 0))              ;; (1)
    (define (count) (vector-ref counter 0))  ;; (2)
    (define reload (call/cc (lambda (k) k))) ;; (3)
    (vector-set! counter 0 (+ 1 (count)))))  ;; (4)

(import (scheme base) (scheme write) (foo))  ;; (5)

(unless (>= (count) 10)
  (display (count))
  (newline)
  (reload reload))
  
;; end of the file
トップレベルの継続はいろいろ落とし穴があるが、確かにこれはという感じものである。では、何が問題になるのか見ていこう。

継続とは

継続はSchemer以外のLisperには馴染みのない概念かもしれないので簡単に説明しておく。Schemeの仕様書を読むとなんとも面倒な言葉で書かれているが、厳密には多少違うが言ってしまえばコールグラフのことである(Clojure/Conjではスタックだと断言していた。まぁその通りである。)。 上記のコードでは以下のようにプログラムが実行される:
  (??) --> (1) --> (2) --> (3) --> (4) --> (??)
                          ^^^^^
                           捕捉
call/ccは上記のコールグラフで捕捉と書かれた部分の継続をreloadという大域変数に保存している。reloadを起動することによって(4)からの処理を再開することができる。

問題

さて、上記のコールグラフで何が問題になるのだろう?(5)があるのにわざわざ(??)書いたのには理由がある。この部分はとても曖昧なのだ。

例えばこのプログラムが1ファイルに書かれていたとしよう。そして、処理系は一つの式を読み込み、実行という順序でファイルを処理したとする。この場合に(??)にくるのは次の式を読み込むという処理になるはずだ。そうすると、保存したreloadを起動した際に起きると予測されるのは、コメント;; end of the fileが読み込まれEOFが返されることであると言える(注:unlessから始まる式は既に読まれている)。ファイルの読み込みがEOFを返した場合、通常そのファイルは全て実行されたと解釈されるので、処理は終了するのが筋である。とすれば、この継続をこの位置で起動すると処理が終了すると言える。(必ずしも処理が終了するわけではないことに注意)

さて、c.l.s.の投稿ではLarcenyで上記のプログラムを走らせた場合想定通りに動いたとされている。これはどういうことか?簡単に言えばLarcenyはvan Tonderの展開器を使っているからとなる。もう少し突っ込んで解説をすると、van Tonderの展開器はR5RS上でポータブルにライブラリ機能を追加している。R5RSにはライブラリ機能は存在しないので実行時になんとかやっているわけだ。例えば上記のプログラムは概ね以下のように解釈される。
(begin
  (let ()
    (define counter ...)
    (define (count) ...)
    (define reload ...)
    (vector-set! ...)
    (register-library '(foo) ...)
  )
  (resolve-import ...)
  (unless (>= (count) 10) ...))
このようにプログラムが展開された場合、(??)に当たる部分は何になるだろうか?そう、(register-library ...)の部分である。そして、プログラム全体が一つの式として解釈されているので、次の式を読み込む必要がない。これによりLarcenyでは見かけ上期待した結果が帰ってくるということになる。この問題はこの2つだけが予測される結末ではないのが面白いところである。

インポート

ライブラリはインポートされることで使用可能になる。では、ライブラリの定義が別ファイルにあった場合はどうなるのだろう?ここからは完全に処理系依存の挙動になるので、拙作Sagittariusの挙動はこうなるであろうというのを例に上げる。Sagittariusではライブラリが別ファイルにあった場合、import句がライブラリのコンパイル等を解決する。その際、現状では継続の境界を作る。

継続の境界とは、C側でSchemeのプログラムをCのスタックをまたぐように呼び出す際に作られるある種の境界線のことである。これが発生しかつ、継続の起動がこの境界をまたぐとSagittariusではエラーを投げる。つまり、ファイルがわかれている場合のケースではSagittariusではエラーになる。他の処理系ではエラーにならないかもしれない。

REPL

REPL上ではどうだろうか?REPLでは(vector-set! ...)が実行された後に次の式を読み込むために一旦制御が入力待ち状態になる。つまり、reloadの起動は(vector-set! ..)を実行し入力待ちの状態になるはずである(少なくともSagittarius上ではそうなる)。これももちろんREPLの実装依存になるだろう。

まとめ

トップレベルの継続を捕捉するのはいろいろ落とし穴がある。R7RSに上記の文言が追加された方が幸せになれるかもしれない。

2016-12-02

R7RS-largeについて

この記事はLisp Advent Calendarの2日目の記事です。

Schemeの最新規格であるR7RSは2つのパートに分かれている。R7RS-smallとR7RS-largeである。通常の文脈でR7RSといった場合はR7RS-smallを指すことが多い。ではR7RS-largeとはなんなのか?

目的

R7RS-largeの目的は以下である:
Working group 2 will develop specifications, documents, and proofs of practical implementability for a language that embodies the essential character of Scheme, that is large enough to address the practical needs of mainstream software development, and that can be extended and integrated with other systems.
The purpose of this work is to facilitate sharing of Scheme code. One goal is to be able to reuse code written in one conforming implementation in another conforming implementation with as little change as possible. Another goal is for users of this work to be able to understand each other's code based on a shared and unambiguous interpretation of its meaning.
The language is not necessarily intended for educational, research, or embedded use, though such uses are not prohibited. Therefore, it may be a "heavyweight" language compared to the language designed by working group 1.
From Charter for working group 2
とんでもなく要約すると、R7RS-smallだけでは実用的なプログラムを書けないので実用的なライブラリ等を整備するよ、という感じである。

どんな感じで進んでるのか

基本的にはWG2Docketsにある項目をSRFIに書き起こして議論し、最終的に投票をするという感じで進んでいる。現状ではRedDocket(データ構造)の投票までが終了している。それらのライブラリ名はWG2のMLに投げられただけで、今のところ明文化はされていない。コミュニティが小さい+(残念ながら)あまり興味のある人がいないというのが大きな問題だろう。最新のSRFIではOrangeDocket(数値)に関するものが出てきている。

ちなみに、風の噂で流れている(た?)ERマクロがR7RS-largeに入るという話は、YellowDocket(構文)に入っているので、Orangeが終わったら着手される可能性がある。RedがR7RS-small制定から3年かかっているので、決まるのは単純計算で6年後ということにはなるが…

決定されたライブラリ

上記のMLを見ればわかるんだけど、文量を増やすために現在までにR7RS-largeに入ることが決定したライブラリとその概要を書いてみたりする。上記のリストからコピーしている(タイポ含む)
  • SRFI 1 (scheme list)
    古くからあるリストSRFI。ほぼ全ての処理系で使えるといってもまぁ過言ではないくらい有名なやつ。
  • SRFI 133 (scheme vector)
    SRFI-43だとR7RS的に互換性がないからという理由で割と最近作られたベクタSRFI。
  • SRFI 132 (scheme sorting)
    ソートSRFI-32が棄却されて12年経った後に出てきたソートSRFI。大体一緒なんだけど、細かいところでAPIが違う。
  • SRFI 113 (scheme set)
    リスト、ベクタがあるのにセットがないのはどうよっていうので出てきたSRFI。
  • SRFI 114 (scheme set char)
    ほぼ間違いないSRFI-14のタイポ。これまた古くからある文字セット用SRFI。
  • SRFI 125 (scheme hash-table)
    R7RSのハッシュテーブルはSRFI-69の後継にあたるSRFIになった。R6RSのハッシュテーブルがあるのにという気持ちでいっぱいの僕としては気に入らない決定の一つ。
  • SRFI 116 (scheme list immutable)
    不変リストなSRFI。中身はほぼSRFI-1と一緒なんだけど、受け取るものが不変リストであるというところが違う。一応通常のcarとかが不変リストを受け取ってもよしなに計らってくれることを推奨しているけど、サポートしてる処理系あるのかな?
  • SRFI 101 (scheme list random-access)
    ランダムアクセス可能なリストSRFI。正直なんでこれがR7RS-largeに入ったのかはよく分からない。
  • SRFI 134 (scheme deque immutable)
    不変な双方向キューSRFI。
  • SRFI 135 (scheme textual)
    O(1)アクセスを保証するテキスト処理SRFI。R7RS-small的には文字列のO(1)アクセスは明示的に要求していないとしているので、パフォーマンスが気になる場合はこっちを使うという感じ。
  • SRFI 121 (scheme generator)
    Gaucheのgeneratorが元のSRFI。
  • SRFI 128 (scheme lazy-seq)
    ほぼ間違いなくSRFI-127のタイポ。GaucheのlseqにインスパイアされたSRFI。SRFI-41のストリームとは多少違うらしいが、ほぼ気にしなくてもいいらしい。
  • SRFI 41 (scheme stream)
    遅延ストリームSRFI。このSRFIはSRFI史上初のR6RSライブラリを使って参照実装が実装されている。
  • SRFI 111 (scheme box)
    ボックスSRFI。ボックスは名前が示す通り単なる箱で、Schemeのオブジェクトをなんでも格納しておくことができるもの。これを使うと明示的に参照渡しが可能になるはず。まぁ、あると便利だけど、なければ(vector obj)で代用できてしまうようなもの。コンパイラが頑張ると最適化してくれるかもしれない。
  • SRFI 117 (scheme list queue)
    単方向キューSRFI。元々はキューという名前だけだったんだけど、SRFI自体がリストを基に実装されていることを前提にしていたので改名されたという経緯がある。SLIBにあった奴とほぼ同じ。
  • SRFI 124 (scheme ephemeron)
    実装者泣かせSRFIの一つ。キーと値を持つデータ構造で、基本的な部分では弱い参照と一緒なんだけど、値がキーを参照していた際でもGCされるという特徴を持つもの。噂によるとまともに実装されているのはRacketとMIT Schemeくらいらしい。
  • SRFI 128 (scheme comparator)
    SRFI-114はあまりにも醜いという理由から作られたSRFI。ちなみに片方あればもう片方は実装できる。
個人的に番号が新しい(100番以降くらい)のは時期尚早感のあるものが多いと思っている。というか、使える処理系が少ない。Sagittariusくらいじゃないかなぁ、一応全部サポートしてるの(手前味噌)。

控えているライブラリ

以下には入るかもしれないし、入らないかもしれない議論すらされてないライブラリを載せる。既にSRFIになってるものはそのSRFI+その他の提案。まだ提案なだけなものリストだけで提案は省略。
  •  OrangeDocket: 数値関係
    • Numeric types and operations: 
      • Integer division: SRFI-141
      • Bitwise integer operations: SRFI-142, SRFI-60 or R6RS
      • Fixnums: SRFI-143 or R6RS
      • Flonums: SRFI-144 or R6RS
      • Compnums: John's proposal
      • Random numbers: SRFI-27 plus Gauche's generator
      • Prime numbers: Gauche's prime number library
    • Numeric and semi-numeric data structures
    • Enumerations (なんでこれここにあるんだろう?)
    • Formatting (同上)
  • YellowDocket: 構文関係
    • Low-level macros (有名どころの低レベルマクロがリストされてる)
    • Syntax parameters: SRFI-139
    • Matching (パターンマッチ)
    • Combinators
    • Conditional procedures (if等をdefineで定義した感じのなにか?)
    • cond guards: SRFI-61
    • Lambda* (Scheme Workshop 2013に出た論文がもとっぽい)
    • Void (eq?で比較可能なNULLオブジェクト)
    • Named parameters: John's proposal or SRFI-89
    • Generalized set!: SRFI-17
    • and-let*: SRFI2
    • receive: SRFI-8
    • cut/cute: SRFI 26
    • Loops: SRFI-42, foof-loop or Chibh loop
    • Generic accessors/mutators: SRFI-123
    • Conditions (例外、なぜにR6RSではないのか…)
  • GreenDocket: ポータブルに書けない何か
    • File I/O
    • Threads: SRFI-18
    • Real-time threads: SRFI-21 (分ける必要あるのか?)
    • Socket: SRFI-106
    • Datagram channels (UDP sockets)
    • Timers: SRFI-120 (なんでこれがあるんだろう?)
    • Mutable environments
    • Simple POSIX (Windowsは完全無視ですね、わかります)
    • Access to the REPL (これ完全にR6RS殺しに来てるな…)
    • Library declarations
    • Symbols
    • Interfaces
    • Process ports (紛らわしいけど、プロセスI/Oをポートにリダイレクトする話)
    • Directory ports (ディレクトリをポートみたいに読む、正直紛らわしい)
    • Directory creation/deletion
    • Port operations
    • System commands (Process portsと被り気味な気がする)
    • Pure delay/force (スレッドを使ったdelay/forceっぽい)
  • BlueDocket: ポータブルだけど高度なものたち(portable but advanced things)
    • Time types: SRFI-19
    • Binary I/O
    • Character conversion (要するにR6RSのコーデック的なもの)
    • Parallel promises (pure delay/forceじゃだめなん?)
    • Pathname objects
    • URI objects
    • Unicode character database
    • Environment (紛らわしいけどusernameとかメモリとかを引くためのもの)
    • Trees (リストにあるけど提案なし)
他にもあるけど、多分色付きの名前の奴が優先だとは思うので省略。

2016-11-29

Syntax parameter 2

Couple of months ago, I've written about syntax parameter. Now, I noticed that this implementation isn't really useful in certain (probably most of the) cases. For example, consider like this situation: You want to parameterise user inputs and your macro need to cooperate with auxiliary macros something like this:
;; default input is '()
(define-syntax-parameter *input* (syntax-rules () ((_) '())))
(define-syntax aux
  (syntax-rules ()
    ((_ body ...)
     (let ((user-input (*input*)))
       ;; do what you need to do
       body ...))))
(define-syntax foo
  (syntax-rules (*input*)
    ((_ (*input* alist) body ...)
     (syntax-parameterize ((*input* (syntax-rules () ((_) 'alist))))
       (foo body ...)))
    ((_ body ...) (aux body ...))))

(foo (*input* ((a b) (c d))) 'ok)
;; *input* is '()
I would expect that in this case *input* should return ((a b) (c d)). And, indeed, Racket returns ((a b) (c d)) as I expected.

The problem is simple. My implementation of syntax-parameterize is expanded to letrec-syntax with bound keyword. However since the above case crosses macro boundaries, *input* always refers to globally bound identifier. Now, my question is; can it be implemented portable way? If I read "Keeping it Clean with Syntax Parameters", then it says it's implemented in a Racket specific way. (If I read it properly, then syntax parameter is really a parameter object which can work on macro.)

Hmmm, I need to think how I can implement it.

2016-11-21

ニュースを読もう

ここ数週間の空き時間を使ってちょっとしたサイトを作っていた。news-reader.nl
ちなみに、ニュースを比較しながら読むとか、ダイジェストだけ知りたいとかいうモノグサな思いから生まれたものだったりする。今のところシンプルな機能しかないけど、そのうち自動でカテゴリわけしたりとか、似ている記事を列挙したりとかできたらいいなぁとは思っている。

適当によく見るニュースサイトを載せてるだけなので、これがあるといいというのがあればリクエストください。

これだけだとなんなので、ちょっと内側も覗いてみたりする。

構成としてはリバースプロキシとしてNGINX、フロントエンドはAngularJS、バックエンドには当然Sagittariusが使われている。NGINXは静的ファイルを返すようにして、Angujarが適当にAJAXでゴニョゴニョする。っで裏で動いているPaellaがPostgreSQLにアクセスしてデータを返すと。

ニュース自体はRSSを取ってきて、タイトルと説明を表示している。裏でCronが一時間に一回走るようになっているので、適当に更新される。(今のところ削除されないから、記事が貯まるとサーバーがパンクするような気もするが、まぁこれは後で考えよう…)

デプロイはAnsibleである程度自動化されている。テスト中はフルオートだったんだけど、本番にデプロイする際に手作業が発生したので。まぁ、そのうち気が向いたら直すことにしよう。(二度目があるとも思えんしなぁ)

広告載せてるけど、これかなりうざいのでサーバー代をお布施するつもりがないのであればアドブロック推奨で。

2016-10-19

Connection and session model

CAUTION: It's rubbish. Just my brain storming.

I'm not even sure if this is a proper model name or not. But I often see in low level communication specification, there is a connection and sessions (or channels) belong to the connection. Sometimes (or most of the time?) channels can be recovered on different connections, but here I discuss non recoverable channel for my ease.

I've written couple of scripts which handles this type of things already. For example AMQP. I think one of the pros for this model is preventing actual connection count. Recently, I've faced a concurrency problem caused by reading data from one single connection. At that moment, I didn't want to create much of connections (but in the end realised that there's no other way, though). Then I've started thinking about it.

Now I'm thinking can this be done in generic way or at least something which can be reused instead of writing the same mode everywhere? Suppose,a connection have a port. If a session is derived from the connection, then connection gives the session a session id or so. Whenever data are read from sessions, then actual connection dispatches read data according to the required session id. Something like this:
(import (rnrs))

(define-record-type connection
  (fields port ;; suppose this is input/output port
          sessions)
  (protocol (lambda (p)
              (lambda (port)
                (p port (make-eqv-hashtable))))))
(define-record-type session
  (fields id connection))

(define (open-connection port) (make-connection port))
(define (open-session connection)
  (let* ((sessions (connection-sessions connection))
         ;; FIXME
         (id (+ (hashtable-size sessions) 1))
         (session (make-session id connection)))
    (hashtable-set! sessions id session)
    session))

(define (read-from-session session)
  ;; IMPLEMENT ME
  (read-for-session
   (session-connection session)
   (session-id session)))
To keep it clear, here connection is the physical connection and sessions are logical layers derived from a connection. A channel is a communication pipe between connection and session.

Obviously, this doesn't work as it is even if I implement reading part, because:
  1. There's no way to know which message should go which session
  2. There's no negotiation how to open a session, this type of things are usually client-server model.
To know how to dispatch messages, connections need to know how message frames look like. So connections should have something like receive-frame field which contains a procedure receiving a message frame?
(define-record-type connection
  (fields port
          ;; suppose this returns 2 values, message id and payload
          receive-frame
          negotiate-session
          sessions)
  (protocol (lambda (p)
              (lambda (port receive-frame negotiate-session)
                (p port receive-frame  negotiate-session (make-eqv-hashtable))))))
Is it good enough? But what if there are like these 2 process is simultaneously running; opening a session and reading payload from a session. So it might be a good idea to have channel like this?
(define-record-type connection
  (fields port
          receive-frame
          ;; takes session-id, channel-id and connection.
          ;; returns physical-session-id
          negotiate-session
          sessions
          channels)
  (protocol (lambda (p)
              (lambda (port receive-frame negotiate-session)
                (p port receive-frame
                   negotiate-session
                   (make-eqv-hashtable)
                   (make-eqv-hashtable))))))
(define-record-type session
  (fields id channel-id connection
          (mutable physical-session-id)))
(define-record-type channel
  (fields id buffer))

(define (open-connection port) (make-connection port))
(define (open-session connection)
  (let* ((sessions (connection-sessions connection))
         (channels (connection-channels connection))
         ;; FIXME
         (session-id (+ (hashtable-size sessions) 1))
         (channel-id (+ (hashtable-size channels) 1))
         (channel (make-channel channel-id
                                ;; IMPLEMENT ME
                                (make-buffer)))
         (session (make-session session-id channel-id connection #f)))
    (hashtable-set! sessions session-id session)
    (hashtable-set! channels channel-id channel)
    session))
Hmmm, would it be enough? I'm not sure yet. And I'm not even sure if this is useful. Maybe I need to implement a prototype.

2016-10-10

SXMLオブジェクトビルダー

なんとなくSXMLをレコード変換するフレームワーク的なものがあると便利かなぁと思って作ってみた。まだ作りこみが甘い部分もあるが、必要そうな部分は動いているのでちょっと紹介。

ライブラリは(text sxml object-builder)としてみた。基本的な使い方はこんな感じ。
(import (rnrs)
        (text sxml ssax)
        (text sxml object-builder)
        (srfi :26))

(define-record-type family
  (parent xml-object))
(define-record-type parents
  (parent xml-object))
(define-record-type child
  (parent xml-object))
(define-record-type grand-child
  (parent xml-object))

(define family-builder
  (sxml-object-builder
   (family make-family
    (parents make-parents
     (? child make-child
        (? grand-child make-grand-child))))))

(define sxml (call-with-input-file "family.xml" (cut ssax:xml->sxml <> '())))

(sxml->object sxml family-builder)
;; -> family object
family.xmlはこんな感じ。
<?xml version="1.0" ?>
<family>
  <parents father="Daddy" mother="Mommy">
    <child name="Big bro">
      <grand-child name="Bekkie">Boo!</grand-child>
      <grand-child name="Kees">Bah!</grand-child>
    </child>
    <child name="Sis"/>
  </parents>
</family>
sxml-object-builderマクロはなんとなくいい感じにオブジェクトビルダーを構築してくれる。基本的には(tagname constructor . next-builder)みたいな構文。next-builderが空リストだと渡ってきたSXMLそのまま使う感じ。後は量指定子だったり、複数タグにしたり。最終的にはXSDと同等の表現力を持てたらなぁとは思っている(ほぼカバーしてるつもりだけど、仕様書を真面目に読んだことがないので何が足りてないのかが分かっていないという…)。

ここではレコードを定義してるけど、constructorは3引数受け取る手続きなら何でもいい。便利かもしれないということで、xml-objectなるものをデフォルトで用意してるけど、特に使う必要はない。

これだけだとイマイチ嬉しくないんだけど、もうひとつSchemeオブジェクトからSXML変換するライブラリを考えていて、それがあれば多少嬉しくなる予定ではいる。

2016-10-06

スペシャリテ

料理の話ではない。

Scheme処理系は世の中に山ほどある。RnRS準拠(n≧5)に絞ったとしてもかなりある(というかR5RSが多い)。そこで、有名処理系が選ばれる理由を考えてみた。

有名所の処理系

R5RS
  • Chicken
    • Eggが便利
    • Cに変換すれば高速に動く
    • コミュニティが大きい
R6RS
  • Chez
    • 現状最高速の処理系
  • Guile
    • GNU公式拡張言語
    • コミュニティが大きい
  • IronScheme
    • .Netで動く
  • Larceny
    • 高速
    • R7RSハイブリッド
  • Racket
    • 高速
    • コミュニティが大きい
    • Planetが便利
    • リッチな環境がついてくる(DrRacket)
  • Mosh
  • Ypsilon 
  • Vicare
    • ファンが多い?
R7RS
  • Chibi
    • コンパクト
    • C拡張が書きやすい
  • Gauche
    • 日本語ドキュメント
    • 手軽?
  • Kawa
    • JVMで動く

あんまりよく考えてないけど考察

Chicken、Guile、Racketはコミュニティの多きさ≒問題解決のしやすさとライブラリの豊富さが大きいかなぁと。Chezは元商用の処理系だけあって速い。Gaucheは日本語ドキュメントが大きいかなぁ(日本限定で考えれば)

KawaとIronSchemeは環境固定されるのでその環境で使いたいならほぼ一択状態。流石にJVMだと他にもあるけど、知名度的にはKawa一択だろう。

Larcenyがどれくらい使われているかはちと分からんけど、超有名な処理系なのでそこそこユーザーはいるんじゃないかなぁ。

Mosh、Ypsilon、Vicareはリリースが年単位でされてないから(MoshとVicareはリポジトリの更新があるけど、Ypsilonに関しては多分放棄されてる)、正直これらを選ぶ理由が見つからない。バグリポートしても直らんし。強いて言えばファンが多いくらい?

スペシャリテ?

じゃあどうするか?そもそも知名度が低いという点でどうしようもないんだけど、宣伝するにしても「それなら〇〇でいいじゃん」ってことにならないようにしたい。っで、現状ある武器で考えると以下かなぁ:
  • R6RS/R7RSハイブリッド
  • リーダーマクロ
  • そこそこ手軽
  • Cトランスレータ有り(バイナリ走らせるのにランタイムがいるけど)
挙げてみて思ったけど、ユーザー視点でいいことないなぁ。これだと「〇〇でいいじゃん」にしかならない。将来的に入れようかなぁと思っているのは日本語ドキュメントなんだけど、入れたところでGaucheの牙城を崩せる気はしないしなぁ。

地道にライブラリを増やすとか、環境を整えるとかなぁ。

2016-09-28

C translator (2)

Starting 0.7.8, Sagittarius provides experimental C translator. Currently it's more or less toy quality (though, just using is not a problem). There are 2 way of translations:
  1. Dump all required library cache
  2. Doing the same as precompiled files
Both solutions have problems. The first one requires huge amount of static area and generated files are huge. The second one can't translate macros.

Dumping cache file is not a good solution so I want to discard it. To do so, second solution must support macro translation. The challenge of doing it is that macro contains shared structures and may contain subrs (Scheme procedures defined in C). Translating shared structure to C may not be so difficult but subrs. Subrs themselves don't have any information where it's defined, so they only have their name and C pointer. As far as I know, there's no subr in macro so this may not be a problem as long as users (me mostly) don't do some black magic (and I know I can...).

One of the benefits (or maybe this is the only) of translating C is that considerably short amount of starting time. Sagittarius is, unfortunately, not so fast implementation, and its starting time is not so fast even when it loads only cached libraries. For example, one of my daily routine scripts takes 500ms to just showing help message (8000ms if it's not cached) and translated version takes 130ms. It's not that much difference (yet) but still it's almost 5 times faster than raw Scheme.

However, I think it's still slow or doing some unnecessary things. Consider the following script:
(import (rnrs))

(define (main args)
  (display args) (newline))
If I run this script, then it loads 32 libraries currently. But the only bindings required in this script are display and newline. So if the C translator is smart enough to detect them, then the result script should look like this:
;; imaginary script... 
(define (main args)
  (#{(core)#display} args) (#{(core)#newline}))
Then it doesn't require any unnecessary library loading.

Hmm, I think I have a lot to do.

2016-09-19

Scheme Workshop 2016 奈良

行ってきた。朝一で奈良入りでもいけなくはなかった気がしないでもないが、せっかくだしということで奈良に2泊3日してきた。奈良の観光の話でもいいけど、流石にどこも回れなかったのでワークショップの話。

前日の夜にAlexからスケジュールの変更のメールが届く。発表者二人が飛行機に乗り遅れたので、その二人を最後に回すため。結局間に合わずWillとKathyによりカラオケスライドメソッド(後述)が発動した。

ホテルから徒歩15分という距離なのに9時15分のオープニングに間に合わず。油断しすぎでした。

【招待講演1】
プログラムの正当性を検証するプログラムにLispを使ったという話(だと思う)。スケジュールが移動して自分の発表が一発目になったのでそれに意識が行き過ぎてたから話が頭に入らなかった…これ聞きながら、今年のはこんなにアカデミックなのかぁとものすごく緊張した記憶しかない。

【自分の】
そのうちスライド挙げます。単にライブラリの紹介。

【Nash: a tracing JIT for Extension Language】
GuileにトレーシングJITを実装したという話。プログラムで使用される時間の大部分はタイトなループによって発生しているということに注目したJITらしい。本家にマージされるかは不明らしい。されてほしいような、されてほしくないような(Sagittariusが選ばれる可能性が下がるという意味で)。

【Ghosts in the machine】
現在のプログラミング環境はコンピュータ黎明期と比較して劣っている、ということをClojureのREPLを例にして示す話。時間内に終わらなかった発表一つ目。休憩中に発表者に質問してどこを目指しているのか質問して聞いたりしていた。視点としては面白いし、一意見として終わらせるには惜しい気がするけど、何から始めるといいんだろう?となるくらいには壮大なビジョンだった。

【R7RS update】
AlexによるR7RSの近況。まだ見ぬSRFI-142とSRFI-143が並んでいたのでArthurで止まっているのか、Johnがまだ提出してないのかは謎。Red Docketは終わったらしいのだが、正式な発表はまだされてない気がするけど、c.l.sで見逃したかな?

【招待講演2】
Guixという比較的新しいパッケージマネージャの話。インストール履歴をリビジョンで管理しているのでうっかり何かを壊しても動くリビジョンに戻せるというのが他のパッケージマネージャとの大きな違いかな。後はGuileで書かれているのでパッケージの定義もS式(Guileで動くプログラム)というのも大きな特徴かな。

【Function compose, Type cut, And the Algebra of logic】
発表者のコミュニケーション能力があまり高くなかったので何が何だかさっぱり分からなかった。ただ、発表資料自体は面白いことが書いてあったので、論文を後で読むことにする。

【Multi-purpose web framework design based on websocket over HTTP Gateway】
SagittariusにWebsocketを入れたこともあり一番気になってた発表なんだけど、今一発表が雲を掴む様な感じでよく分からなかった。また、コミュニケーションの問題も多少あり、納得のいく答えも得られず。後で論文を読む。

ここからカラオケスライドメソッドが発動する。ちなみに、カラオケスライドとは自分が作ったスライドではないものを発表するもと思えばよい。命名はWill。日本のカラオケが流れてくる文字を歌うことに引っ掛けた上手い名前だと個人的には思った。

【miniAdapton: A Minimal Implementation of Incremental Computation in Scheme】
AdaptonというMemoriseのテクニックに一つをSchemeで実装した話。入力ツリーの一部が変更されても共通部分は再利用しているような感じだった。リポジトリも公開されてるし、ソース見た方が早いかな。しかし、代理の発表(論文の共著者であるが) なのにえらくうまいことやるなぁと前回見た時も思ったなぁ。あれくらいうまくしゃべれるようになりたい(なんか違う)。

【Deriving Pure, Functional One-Pass Operations for Processing Tail-Aligned Lists】
リストの共通サフィックス部分を調べるスクリプトをエレガントに書いたという話。ベンチマークがないのでこれが実際にどこまで効くのかは微妙。ナイーブな実装、エレガントな実装ともにO(N)なので、コンスタントの部分だけなのだが、正直論文の実装だとヘルパー手続きの作成にかかるコストの方が高い気がしないでもない。まぁ、コードは論文に載っているので手元でベンチマークとってJasonにメール投げてみればいい話な気がする。

どうでもいいのだが、僕が喋る英語はオランダ語訛りらしい(Arthur談)。どうも子音の発音がネイティブに比べて強いのだそうだ。普通母語に引っ張られるが、第三言語(第二が英語)に引っ張られるのは珍しいんじゃないの?という話をしていた。確かに寡聞にして聞かない話ではある。

2016-08-30

ログ

そういえばSagittariusにはずっとログを吐き出すためのライブラリがないということをふと思い出した。附属させてるライブラリがログを吐き出すのはさすがにどうかと思うのであんまり考えてなかったのだが、Paellaみたいなのが何のログも吐かないというのはいろいろ面倒だなぁと思ってはいた。

あまりいいデザインというのも思い浮かばないんだけど、なんとなくこんな感じのロガーがあればいいかなぁと思い30分くらいで作ってみた。こんな感じで使える。
(import (srfi :18) (util logging) (util file))

(define (print-log logger)
  (trace-log logger "trace")
  (debug-log logger "debug")
  (info-log logger "info")
  (warn-log logger "warn")
  (error-log logger "error")
  (fatal-log logger "fatal")
  (terminate-logger! logger))

(print-log (make-logger +info-level+ (make-appender "[~l] ~w4 ~m")))
(print)
(print-log (make-async-logger +debug-level+ 
         (make-appender "[~l] ~w4 ~m")
         (make-file-appender "[~l] ~w4 ~m" "log.log")))

(print (file->string "log.log"))
#|
[info] 2016-08-30T16:02:29+0200 info
[warn] 2016-08-30T16:02:29+0200 warn
[error] 2016-08-30T16:02:29+0200 error
[fatal] 2016-08-30T16:02:29+0200 fatal

[debug] 2016-08-30T16:02:29+0200 debug
[info] 2016-08-30T16:02:29+0200 info
[warn] 2016-08-30T16:02:29+0200 warn
[error] 2016-08-30T16:02:29+0200 error
[fatal] 2016-08-30T16:02:29+0200 fatal
[debug] 2016-08-30T16:02:29+0200 debug
[info] 2016-08-30T16:02:29+0200 info
[warn] 2016-08-30T16:02:29+0200 warn
[error] 2016-08-30T16:02:29+0200 error
[fatal] 2016-08-30T16:02:29+0200 fatal
|#
ロガーがログレベルと同期をコントロールして、アペンダーが実際にログを吐き出す。どこかでみたことあるようなモデルではある。まぁ、本職はその言語を使うのでなんとなく似通ったのだろう。make-appenderでつくられるアペンダーは標準出力(正確にはcurrent-output-portが返す値)に吐き出す。アペンダーの第一引数はログのフォーマット。現状手の込んだ出力はできないが、当面はこれでも問題ないだろう。ちなみに、~wの後ろに続く4はSRFI−19で定義されているフォーマットの一つ。現在は一文字しかみないが、そのうちなんとかするかもしれない。

make-async-loggerはスレッドを一つ消費する代わりにログの書き出しをバックグラウンドでやってくれる。アペンダーが複数あったり、処理が重い(メール送るとか) 等あるときに役に立つと思う。

とりあえずこれを適当に使ってるスクリプト等に組み込んで使用感を確かめていくことにしよう。

2016-08-29

Ephemeron and reference barrier

Recently, there's the post on SRFI-124 ML about reference barrier. The SRFI doesn't require reference-barrier procedure due to the non-trivial work to implement. Now, there's a post which shows how to implement portably, like this:
(define last-reference 0)
(define (reference-barrier x)
  (let ((y last-reference))
    (set! last-reference x)
    y))
It seems ok, but is it? The SRFI says like this:
This procedure ensures that the garbage collector does not break an ephemeron containing an unreferenced key before a certain point in a program. The program can invoke a reference barrier on the key by calling this procedure, which guarantees that even if the program does not use the key, it will be considered strongly reachable until after reference-barrier returns.
Due to the lack of knowledge and imagination, I can't imagine how it should work precisely on like this situation:
(let loop ()
  (when (some-condition)
    (reference-barrier key1)
    (reference-barrier key2)
    (do-something-with-ephemerons)
    (loop)))
Should key1 and key2 be guaranteed not to be garbage collected or only the last one (key2 in this case)? If the first case should be applied, then the proposed implementation doesn't work since it can only hold one reference. To me, it's rather rational to hold multiple reference since it's not always only one key needs to be preserved. However if you extend to hold multiple values, then how do you release the references without calling explicit release procedure? If the references would not be released, then ephemerons would also not release its keys. Thus calling reference-barrier causes eternal preservation.

Suppose this assumption is correct, then reference-barrier should work like alloca with automated push so that the pushed reference is on the location where garbage collector can see them as live objects. In this sense, allocated references are released automatically once caller of reference-barrier is returned and multiple references can be pushed. Though, it's indeed non-trivial task to implement. I hope there's no errata which says reference-barrier is *not* optional, otherwise it would be either a huge challenge or dropping the support...