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.
Syntax highlighter
2016-12-27
2016-12-25
OAuth
ちょっとしたメモ。
SagittariusはCommon Lispから移植したOAuth1.0を扱うライブラリを持っている。っが、このライブラリ将来OAuth2に対応させれるようにしたのかキーワード引数が大量にあり今一使い勝手が悪いい。っで、最近職場でOAuth2上に構築されたプロトコルの一つであるOpenID Connectを使うということが持ち上がっていることもあり、OAuth2のライブラリがいるかなぁという機運が高まってきている。
割と周知の事実だと思うが、OAuth1.0とOAuth2は互換性がない。RFC5849で定義されているOAuth1.0はRFC6749で定義されているOAuth2によって廃止されているのではあるが、名前の似た二つのプロトコルとしてみた方がよい。事実、RFC6749には以下の段落がある:
どちらのプロトコルもクライアントを実装するだけならそんなに難しくないと思っていて、問題になるのはAPIの設計だと思っている。中身を見れば全くの別物なのだが、名前や歴史的経緯を見ると両社は密接なつながりがあるように見える。そうすると、ユーザー(クライアント)としてはシームレスに使えた方がいいかなぁとか考えてしまうわけだ。最終的にアクセストークンを使って保護されたリソースにアクセスするわけだし。そうするとこんな感じにライブラリを構成するのがいいだろうか?
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に以下の一文を追加しようという話になる。
例えばこのプログラムが1ファイルに書かれていたとしよう。そして、処理系は一つの式を読み込み、実行という順序でファイルを処理したとする。この場合に
さて、c.l.s.の投稿ではLarcenyで上記のプログラムを走らせた場合想定通りに動いたとされている。これはどういうことか?簡単に言えばLarcenyはvan Tonderの展開器を使っているからとなる。もう少し突っ込んで解説をすると、van Tonderの展開器はR5RS上でポータブルにライブラリ機能を追加している。R5RSにはライブラリ機能は存在しないので実行時になんとかやっているわけだ。例えば上記のプログラムは概ね以下のように解釈される。
継続の境界とは、C側でSchemeのプログラムをCのスタックをまたぐように呼び出す際に作られるある種の境界線のことである。これが発生しかつ、継続の起動がこの境界をまたぐとSagittariusではエラーを投げる。つまり、ファイルがわかれている場合のケースではSagittariusではエラーになる。他の処理系ではエラーにならないかもしれない。
最近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とはなんなのか?
ちなみに、風の噂で流れている(た?)ERマクロがR7RS-largeに入るという話は、YellowDocket(構文)に入っているので、Orangeが終わったら着手される可能性がある。RedがR7RS-small制定から3年かかっているので、決まるのは単純計算で6年後ということにはなるが…
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.とんでもなく要約すると、R7RS-smallだけでは実用的なプログラムを書けないので実用的なライブラリ等を整備するよ、という感じである。
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
どんな感じで進んでるのか
基本的には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。ちなみに片方あればもう片方は実装できる。
控えているライブラリ
以下には入るかもしれないし、入らないかもしれない議論すらされてないライブラリを載せる。既に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 (リストにあるけど提案なし)
Subscribe to:
Posts (Atom)