Syntax highlighter

2017-08-21

浮動小数点とC99とSRFI-144と

Sagittarius 0.8.6でSRFI-144をサポートした。このSRFIをポータブル実装を使ってサポートするとパフォーマンス的に得るものが何もないと思いC側でサポートすることあらかじめ決めていた。ネックになるのはこのSRFIが要求しているのがC99の数学関数であることで、Sagittariusでサポートを明記しているVS2010ではC99がサポートされていない。VS2013からはサポートされているので、4年も前の環境だし上げてもいいかなぁとは思ったが、何かの間違いでこのバージョンを使っている人がソースからコンパイルしているという可能性も0ではないかなぁと思い互換レイヤを書くことにした。

互換レイヤ自体はまぁ、普通の苦労で済んだのだが、そこから先が大変だった。Ubuntu 16.04では動くがTravis上のUbuntu 12.04ではテストがこけるとか、VS2013でのCランタイムではテストがこけるとか完全に環境依存の問題に移行したのである。ちなみに嵌ったのは以下:
  • VS2013以降のtgammaはアンダーフローが起きると0.0を返すが、Linux(glibc)では-0.0が返る(たぶんどっちも仕様上はOK)
  • VS2013以降のynは第二引数が0.0だとNaNを返すが、Linux(glibc)では-inf.0が返る(POSIX見ると第二引数が0.0だとpole errorで-HUGE_VALとあるが、C99が同義かはしらない)
  • glibcの特定のバージョン(2.21以下と思われる)ではremquoとlogbが不正な値を返すことがある。(remquoについてはバグレポートを見つけたが、logbは完全に勘。下手するとeglibc固有の問題化もしれない)
  • fl-leastはDBL_TRUE_MIN(C11)が割り当てられるので、どのバージョンのVSがサポートしてるか不明
とにかく環境がないということの方が辛く、おとなしくポータブル実装を使っておけばよかったという気持ちに何度もさせられた。また、参照実装に付属しているテストが要求する誤差が結構厳しく、適当な実装だと誤差が大きすぎるというのも苦労した(Windows)。

苦労の甲斐があるかはわからないが、Sagittarius上で浮動小数点を扱う際はこのSRFIを使うとCと同等の速度が得られることだけは保証されるようになった。っが、個人的に浮動小数点はあまり使わないので、自分では有難みを享受できないという悲しい話もあったりなかったりする。

2017-07-12

オランダでの収入と支出

ITエンジニアと給料では給料だけにしか触れなかったが、この記事によるとアメリカの一部地域(例:シリコンバレー)では生活コストも高いので世帯年収1200万以下は貧民層となるらしい。生活コストも含めた比較となるとオランダくらいしかできないので、ざっくりとオランダでの生活コストを調べてみた。

生活コスト
日用品等のコストは「Cost of Living in Netherlands」のページが詳しい。基本的には食品はそれなりに安く、家賃は高いという感じらしい。
ざっくりとしたコストとしては「What is the average cost of living in The Netherlands」のページに書いてある。以下は適当な日本語訳したリスト
  • 家賃: €800-1000
  • 電気、水道等: €150
  • インターネット(大抵テレビと電話もついてくる):€30−50
  • 健康保険:€270(3x90 18歳以下の子供は無料)
  • その他保険: €30
  • 交通費: €100
  • 食費:€100週 (€400月)
合わせると€1780−2000くらい。これに交際費等が入ってくるが、感覚的に妥当な数字かもう少しかかるかなぁというところ(上記だと最低限くらいしかない感じ)。

収入
2016年における一人あたりの手取りの平均収入は€2158となっている(参考: Average Salary in European Union 2016)。平均世帯収入は単純に2倍すればいいだろうか?

以下は2017年の額面における手取り額(日本語ただしいか?)
月収(額面)月収(手取り)年収額面
2000169525920
3000222838880
4000273651840
5000324464800
6000372677760
7000417090720
80004614103680
90005058116640
100005502129600
参考:Dutch Income Tax Calculator
年収の算出は月収x12.96になっている(注:オランダでは月8%の休暇手当が義務付けられている)。もちろんボーナスのでる会社もあるので、この年収は月収に対しての最低保証額ということになる。

€2000(上記の生活コスト)+€1000(ある程度の交際費と貯金)くらいを文化的な生活とすると、世帯収入的には年€50000くらいあれば「中の中」くらいだろうか?累進課税がキツ目なので、収入が一人だと年€61000くらいないと厳しい感じである。

現在位置ユーロ130円なので、世帯収入650万円(一人なら793万円)で「中の中」くらいの生活ができるとすれば、アメリカの1200万円で「中の下」と比べるとましなのかね?ただ、年収€61000を新卒(エントリーレベル)で出す会社は今のところオランダでは見たことないので(というか、これくらいだと既にシニアレベル)、やはり給与面ではオランダはアメリカに比べると塩っぱい気がする。少なくともIT産業に於いては。

2017-07-03

引っ越し

7年住んだアパートからの引っ越しが完了した。新居の準備がまだ全部終わっていないが(床のフローリングが一部終了してない等)今週中には終わるだろう。7年も住んでいると普段は気にしなくてもこういう時に物であふれていたんだなぁと気付かされた。引越し業者が荷物の運び出しを完了した後で10箱以上自力で運びだしたりしていた(箱詰めが失敗したとも言えなくないが、細々したものというのは往々にして忘れられがちなのだ)。

オランダのアパートは入る時も出るときも割と面倒である。入るときはだいたい床はコンクリートむき出しの状態、壁紙はあったりなかったり。出るときは以下の状態にして出ろと言われる:
  • 床はコンクリートむき出し
  • 壁は白く塗ってあること
  • 天井も白く塗ってあること
  • 壁に開けた穴は埋めること
まぁ、入った時と同じにしろと言われる。ただ、僕の場合は多少特殊で、入った時は「天井が白ければよい」という条件だったんだけど、途中で大家が変わったので「壁も白くしろ」が追加された。なので、受け取った時は青い壁とか、トイレに模様とかあったんだけど、それらを全て白く塗りつぶせという話になった。納得が行かないが、まぁしょうがない。

偶然にも、新しい居住者が入れ替わりで入ることになりかつその人がフローリング、冷蔵庫、洗濯機、オーブン、ベッドフレームを引き取ると言ったのでそれらの運び出し及び廃棄をする必要がなくなったのは幸運だったのだろう。その人にとってもそれらが無料で手に入るのは悪くない条件だと思う。多少汚れてたりしても…(余談だが、フローリングはアパートが広いこともあって総額で2000ユーロ以上かかってたりする。)

新居の鍵の受け渡しから退去までが一週間しかなく、その間に荷出しとかもあったので、非常に疲れる週であった。連日23時くらいまで運び出しと清掃等を行っていたのだが、22時くらいまでは明るいという非常に幸運な時期に引っ越ししたとも言える。これが冬だったら暗闇の中いろいろやることになっていた。

どうでもいいのだが、敷金が帰ってくるまでに2ヶ月かかるってどういうことなんだろう?どう考えても時間かけ過ぎだと思うだが?

2017-06-20

Kotlin 始めました

2週間前にこんなツイートをした。


このツイートの後えらくだるだるなスプリントを過ごしたので(1スプリント=2週間)、しばらく触っていなかったのだが、今週のプランニングでメンバーから陽に「お前はKotlin使うよな?」と煽られたのでまじめに使い始めた。

書き始めて今日で4日目くらいだけど、Javaと比較してみたりする

【長所】
  • 記述量が減る。無駄なゲッターとかセッター要らない
  • デフォルトでLambda式が使える。(Java8な環境なら関係ないけど)
  • 型推論は良いものだ
  • クラスを別ファイルにしなくてもよいのは便利
  • パッケージ定数とかも便利
  • Javaとの親和性が高い
【短所】
  • 重い。IntelliJが頻繁に固まる
  • Mavenの記述が面倒
  • 素ではMockitoとの相性が悪い(mockito_kotlinで凌ぐ)
  • Javaとの親和性が高いけど、ところどころコンパイル結果を気にする必要がある。(慣れかな)
  • inlineの挙動。混乱するだろ普通…
  • データクラスあるけど、パターンマッチない
  • デフォルトpublic。デフォルトfinalクラス。(好みの問題) 
  • Companionは面倒
Null安全は便利だけど、猫も杓子もNull安全という風潮はだいぶ疑問。まだ使ってない機能がいくつかあるけど、使う時が来たら使う感じかなぁ(Type safe buildersとか使いたいけど、使う場面があんまりないんだよなぁ…)

感想としてはJava++として使うのであれば優秀。 Kotlin的な書き方(チュートリアルにあるようなのだと信じてる)をすれば記述量がだいぶ減る(でもIntelliJが固まるので作業時間的にはトントンというのもある…)。なんだかんだで既存のJavaライブラリがそのまま使えるのはやはりうれしい。

リストとかマップとかのリテラル表記とかあったら便利だろうなぁとか、パターンマッチほしいなぁとか、なんで明示的にreturn書くんだろうとかいろいろ思うけど、今のところはJavaより楽に書けるというメリットの方が大きい感じではある。

2017-06-16

ITエンジニアと給料

Twitterで米国で働くといい給料がもらえる云々の呟きを見た。米国におけるITエンジニアの給料が破格に高いのはまぁ周知の事実として、正確にどれくらいの差があるのかというのはあんまり考えたことがなかった。以前Amazonからもらったオファーは米国なら13万ドル、ルクセンブルグなら7万6千ユーロだったので、単純に2倍弱くらい離れてるのかなぁと思っていた。(正直どうして蹴ってしまったんだと今でもたまに悔やむが、トランプ政権とか見ると行かなくて正解だったのかなぁとも思っている。)

例が一つというものどうかなぁと思ったので、欧州と米国の関数型言語求人に特化したサイトを覗いてちょっと比較してみた(ずいぶん前に登録してた)。
LevelUSEU
Senior$150k-200k€60-75k
Full stack$100k-150k£45k - £60k
Junior$120k£35k - £45k
フルスタックがシニア以下とか、通貨が違うとかはまぁ置いておいて、ぱっと見2倍以上の差はあるのかなぁと。(米国のジュニアはプログラマではないので微妙)。

上記のEUはドイツか英国なんだけど、英国は一応最大で£120kの募集要項もあったので、探せばなくはないかなぁ。

ザクッと要求するスキルを見るとだいたい同レベルのことを求めているので、やれることが同じなら米国の方が2倍くらい給料がいいということにはなる。

物価とか保険とか税金とかどれくらい生活コストがかかるのかは知らないけど、単純に見れば英語ができてチャンスがあるのであれば現状欧州出て米国に行った方がよい給料がもらえる。個人的に気になるのは、欧州はいくらか世界規模のサービス展開している企業があるのに(僕の前職もそう)給与水準は低く抑えられているという点と、ITエンジニアはかなりの奪い合いなんだけどそれに見合った報酬という感じはしないところかな。前にも書いた気がするけど、ヘッドハンターに現状より年間で5千ユーロは高くないと嫌だというと説教食らうところとか(これはオランダだけかもしれんけど)。

いろいろ書いたけど、欧州もそれなりにしょっぱい感じです。オランダ限定なら間違いなくしょっぱいです。

2017-05-19

ちょっとしたジレンマ

仕事でスクラムをやっているのだが、Grooming(最近だとRefinement)、Wikipediaの日本語ページに訳がなかったので英語のままで書くことにする、に微妙に温度差があって多少困っている。
ちなみに、スクラムのプロセスについて書くということはないので、その辺を期待しているのであれば期待はずれになると思われる。

チームでは最近縦のスライスを意識してやることにしている。そうするとどうしても最初はプロトタイプ的なものがチケットになる。このプロトタイプが結構曲者で、今のチームは横のスライスを意識してやってきた経験が長いので、プロトタイプで必要になるコンポーネントがどれくらいに複雑になるかがある程度見えてしまう。というか、開発者であれば、経験上最終的に一つのコンポーネントがどれくらいの機能が必要かが見えるので、それを考慮に入れてしまう感じである。あまりうまく例えられないのだが、レゴで城を作ればいいのに、実際のレンガで城を作ることを考える感じだろうか?

例えば簡単なSMS認証のWebサービスを作るとする。以下がチケットの詳細とAcceptance Criteria(これの訳も知らない):
詳細
ユーザからの携帯番号を受け取り、認証コードを受け取った携帯に送る。ユーザーにリクエストIDとステータスを返す。ユーザーはSMSで送られた認証コードとリクエストIDをサーバに送る。認証コードが一致すればリクエストIDと成功を返す。一致しなければリクエストIDと不一致を返す。なおUIは考えなくてもよい。

Acceptance Criteria
  • SMSを送るには以下のAPIを使う(3rdパーティーAPI)
  • リクエストはデータベースに保存する
  • デモ可能である
後はテスト項目があるけど、それは割愛。

僕はこれに対してPoC(Proof of Concept)でありかつ次のスプリントで改善して行けばいいということを念頭に最短かつ最も単純にやるという方針で5ポイントをつけた。実際JEE開発したことがある人であれば、使うフレームワークにもよるだろうが、この程度1周間もあればできると思うだろう。(ストーリポイントを時間で図るのはいかがかとは思うが、個人的には便利かなぁと思うので一週間=5ポイントにしている。)想定した工程は以下
  • 2つテーブルを作る
  • 3rdパーティAPI用ライブラリを作る
  • REST APIを2つ作る
  • ざっくばらんなMaven処理(モジュール作成等)
フルスクラッチなので、コードの記述量は多いがそれ以外は特に複雑なものはないと判断した。

っが、他のメンバーは13ポイントをつけた。ちなみに今のチームでは13ポイントは1スプリント(ちなみに今のチームでは1スプリント=2週間)ではできないとしている。その理由は:
  • テーブルデザインがいる
  • 3rdパーティAPI用ライブラリのデザインがいる
  • REST APIのデザインと他チームとの強調がいる
  • デプロイ用のサーバ等環境構築
完全にリファクタリングによる手戻りを極力減らそうという感じであった。そうすると確かにきちんとしたデザインとかあるし、それらには時間がかかるのも判る。っが、PoCでやることではない気がとも。あるメンバーはどう考えても1スプリントではできないと言い張ったし…

結局間をとって8ポイントにした。言い出しっぺの法則的に僕がやるということになったが、実際に完了までに1週間(うち1日は病欠したので、正味4日)。自分のスキルレベルをよく把握してるなぁと感心するレベルでの見積もりの正確さであった。

何がジレンマかというか
  1. 見積もりに大幅な開きがある
  2. 他のメンバーは城をレンガで建てようとする(必要以上にかっちり作りたがる)
の2つである。正直どうすればいいのかよく分からない…

2017-04-26

文字列とコーデック

R6RSに於いてポートはバイナリと文字列を明確に区別している。例えば、バイナリポートに対して文字列の書き込みはできない。この2つの橋渡し役がトランスコーダになる。トランスコーダはコーデックを受け取り生成され、transcoded-port等の手続きでバイナリポートを文字列ポートに変換する。ここまでは特に問題ないだろう。

さて、ここからが問題である。以下のコードは何を出力するだろうか?
(display "\xFF;\xD8;")
期待する挙動としては"\xFF;\xD8;"がそのまま出力される、つまり0xFF 0xD8として2バイト出力されることを期待するだろうか?

上記の挙動を期待した人は手をあげなさい。
(・ω・)ノシ
手を上げた人のそのまま残りなさい、補習があります。挙げなかった人はこのまま帰ってもよいです。もちろん補習を受けてもよいですよ。

さて、実際の挙動を見てみよう。大抵の処理系では出力される文字列は"ÿØ"となり、これのバイナリ表現はUTF-8で0xC3 0xBF 0xC3 0x98になるだろう。勘がいい方は気づいたかもしれないが、この挙動の正体はトランスコーダの仕業である。displayに出力ポートを指定しなかった場合current-output-portが使われるのは周知のことであると思われる。current-output-portにはどんな文字列ポートが割り当てられているのだろうか?R6RSによると以下である。
These return default textual ports for regular output and error output. Normally, these default ports are associated with standard output, and standard error, respectively. (omit) A port returned by one of these procedures may or may not have an associated transcoder; if it does, the transcoder is implementation-dependent.
要約:規定の出力ポート。通常は標準出力に割り当てられる。返されるポートにはトランスコーダが紐つけられているかもしれない。もしそうならそれは処理系依存である
処理系依存である。例えばSagittariusでは(native-transcoder)が割り当てられるし、Chezはトランスコーダを割り当てていない。

処理系依存の挙動ではいまいち納得が行かないので、これを処理系非依存の挙動で書いてみる。
(call-with-bytevector-output-port
 (lambda (out) (put-string out "\xFF;\xD8;"))
 (make-transcoder (utf-8-codec)))
このコードを実行すると上記と同様の出力が得られる。文字#\xFF#\ÿなるのかというのはUCS4とUTF-8の変換表を見てもらいたい。

ではどうすれば変換せずに出力できるのか?答えは割と簡単でlatin-1-codecを使うと良い。上記のコードを以下のように書き直すと予定通りに動く:
(call-with-bytevector-output-port
 (lambda (out) (put-string out "\xFF;\xD8;"))
 (make-transcoder (latin-1-codec)))
もちろん、文字列の中に多バイト文字が混じっていた場合はエラーになるので気をつける必要があるが。

余談ではあるが、この問題を回避するポータブルな方法はR7RSにはない。つまり、文字列をバイナリ表現として扱う非処理系依存なコードは現状のR7RSでは書けないということになる。また、少なくともChibiとGaucheでは文字列"\xFF;\xD8;"displayで出力したら"ÿØ"が出力された。R7RS-largeに期待したい類の問題である。

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

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