Let's start Scheme

2018-01-26

Mingwサポート

Ubuntu上でもWindows用のコードをコンパイルしたいと思いMingwのサポートをしてみた。実際にMingwでコンパイルされたバイナリを動かしていないのでコンパイルできる以上の意味を今のところ持っていない(そのうちCIでは回すようにしたいが、優先度的には多少低め)。

とりあえずMSVCとの非互換な部分は大きく以下:
  • struct timespecが存在する
  • .ccファイルをC++として認識する(と思われる)
    • Boehm GCのコンパイルで困らされた
  • __try__finallyがない
    • __try1__except1はうまいこと動かない
    • リンカがエラーを投げる
    • なので今のところ代替手段なし…
  • 細かいライブラリの名前が違う、かつ#pragma commentは動かない
    • ws2_32とか
これら以外は大きなコードの変更もなくコンパイルできた(動いたとは言っていない)。

Twitterで「移植性を考えたらC言語を使うしかない」というのをみたが、C言語を使ってかつ多大な努力を払わないと移植性なんて得られんなぁ…同じWindows上のバイナリを作るのにもこれだよ。

上記にもあるが、コンパイルできるだけで動くとは言っていないので、Mingwの環境がある方だれか試してくれないかなぁ(チラチラ Issue上げたりPR送ってくれたりするとなお嬉しいなぁ(チラチラ

2018-01-25

Scheme環境ツール

各種Scheme処理系をある程度便利に使いたいと思い、こんなんを書いている。なんでそんなことを思ったかというと:
  1. タイムゾーンなSRFIを書こうかな
  2. ポータブルな実装がいるよなぁ
  3. 処理系を簡単にスイッチできないかなぁ ← これ
完全にYak shavingである。今時かどうかは知らないが、目標としてはインストール不要の完全オンデマンドみたいなのを目指している。なので、READMEにも書いたがこんな感じでインストールかつ使用できる。
$ curl https://raw.githubusercontent.com/ktakashi/scheme-env/master/bin/install.sh | sh
$ export PATH=$HOME/.scheme-env/bin:$PATH
$ scheme-env install chibi-scheme
$ scheme-env switch chibi-scheme@master
$ scheme-env run
これでChibi Schemeをソースからビルドしてかつ使用可能にしてくれる。ソースからビルドするので、各処理系が要求する依存関係は予めインストールされている必要がある(流石にそこまで面倒見れない)。ちなみに最初のShellスクリプト以外は全部Scheme(Sagittarius)で書かれている。

今のところインストール可能な処理系はChibiとSagittariusだけだが、すぐにGaucheや他の処理系もサポートされる予定。

動作環境はUbuntuを想定しているが、ホスト処理系(Sagittarius)の要求するパッケージのインストールをしないのであれば、 他のPOSIX環境でも動くはず(自信ない)。Cygwinはプロセスを多用していることもあって多分無理(誰かこの問題直して…)。MingwサポートしてMsys上でとかかなぁ。

ここからはどうでもいい話なのだが、実はこれを作ったおかげで0.9.0のリリースが大分遅れることになった。理由は以下:
  •  Ubuntu上でHomeが暗号化されているとC Assertで落ちる
    • キャッシュファイルのPATHが長すぎるのが原因だけど、そのままだとあまりに不便
  • (rfc tls)でbitbucket.orgに繋げられない
    • TLS 1.2実装の問題
    • 流石に温かみのある手作り実装に疲れてきたのでOpenSSLやSSPIに切り替える
1つ目は直したんだけど、2つ目が流石に時間がかかる。OpenSSLは楽なんだけど、SSPIがねぇ。まぁ、来月くらいにはリリースできるんじゃないかなぁくらいの気持ちではいるが、さて。

2018-01-14

リストの作成

こんなツイートをしたが、どう書くかというのは示してなかったので(iPhone上からだったので)、せっかくだしブログのネタにしようという話。



リストの生成方法にはいくつか方法があるが、今回は有名どころを二つ書いておく。
cons + reverse
cons + reverseはまぁよく使われるパターンで、この名前で呼べば大体どんなものかの想像がつくくらいだ(と思う)。ツイートで言及しているQiita記事にある手続きfをこのパターンで書き直してみた。
(define (f/cons+reverse init n)
  (let loop ((i 0) (r init))
    (if (< i n)
        (loop (+ i 1) (cons (tent_func (car r)) r))
        (reverse r))))
nthが何をしているものなのかよくわからなかったが、おそらく(nth -1 L)はリストの最終要素を取り出すものと予想。(そのように実装して元の手続きを動かしたらそれっぽい値返したし。)
これだとappendがなくなるので、O(n^2)がO(n)になる。一時リストの生成が嫌ならreverse!を使えば生成されるリストもたかだか一個になる。
doで以下のように書くこともできる。
(define (f/cons+reverse init n)
  (do ((i 0 (+ i 1)) (r init (cons (tent_func (car r)) r)))
      ((= i n) (reverse r))))
どちらがいいかは好み次第。
ちなみに、reverseがないので保留と言われた(記憶、Twitter上で見つからん)のだが、reverseはこんな風に書ける。
(define (reverse l)
  (do ((l l (cdr l)) (r '() (cons (car l) r)))
      ((null? l) r)))
RnRS準拠の処理系なら絶対持ってるはずだが、自作の処理系だったという可能性も踏まえてみる。 (積極的に無駄な処理をするコードを直していくスタイル)

unfold
Schemeの標準にはないが、SRFI-1 (R7RS-large)にはunfold という手続きがある。これを使うと上記は以下のように書ける。
(define (f/unfold init n)
  (unfold (lambda (x) (< (car x) 0))
          cdr
          (lambda (x) (cons (- (car x) 1) (tent_func (cdr x))))
          (cons n init)))
こっちは多少メモリ効率が悪い(シードの生成に必ずペアを作ってる)し、unfoldの使用方法が多少変則的な感はある。

ループ内で(append result (list ...))みたいなコードを見つけたら積極的に書き換えていきたいところである。

2018-01-08

日付と時間 その2

前回の続き

Shiroさんの案では calendar 自体が (y, m, d, H, M, S) の組を持つというもので、SRFI-19 の date は (y, m, d, H, M, S) + timezone というもの。っで、 calendar の種類は複数あって、例えばグレゴリアン歴の2018年1月8日は D = (2018, 1, 8, 0, 0, 0)だけど、これと同等のユリウス暦は2017年12月26日 D = (2017, 12, 26, 0, 0, 0)になる。更に、それぞれの calendar が時間の演算を持つというもの。例えば calendar A では一日が30時間だとすると、その calendar における日付B+1日というのは Gregorian calendar では1日と6時間を足したことになるが、この差異を calendar がよしなにしてくれるというもの。(意図を正しく理解していれば)

個人的には非常に可搬性が高く、火星に行っても火星用の calendar を作成すれば使えるいいアイデアだと思うんだけど、地球上でプログラムをするのであれば、RFC3339で定義されている時間で概ね事足りると思ったりもする。(まぁ、宇宙開拓史みたいなゲームを作って火星のカレンダーが必要だみたいなのはあるかもしれんが、レアケースと割りきってもいいよね?)

そうするとこんな感じで簡略化するとよくね?という気にもなる。
日付 D = (y, m, d, H, M, S)
カレンダー C
タイムゾーン TZ
として、日付Dは常にRFC3339で表現できるものとする。日付の演算はデフォルトでは以下のようにできる:
D+1d = (y, m, d+1, H, M, S) = (y, m, d, H + 24, M, S) = ...
カレンダーCは例えばユリウス通日等の計算や、そのカレンダー上での一日を日付D上に加算するのに使ったりするこんな感じ。
(calendar:days->date calendar:julian 0) ;; -> D(-4714 11 24 12 0 0)
タイムゾーンTZはまぁ、普通にタイムゾーンで、SRFI-19の date は D+TZ になる。

目を凝らすと日付Dは (y, m, d, H, M, S) + RFC3339 と言えなくもなく、デフォルトの演算は calendar Crfc3339 によって提供されるものとも言えるので、多少の利便性だけとも言えなくもない。じゃあ、最初からそうすればよくね?という気もしてきた。う〜ん。

2018-01-04

日付と時間

日付や時間はプログラムを書く上で鬼門になりがちなものである。例えば、2018年1月4日11時10分と言った時に、この時間は常に同じ時間を指すだろうか?答えはNoである。例えばオランダと日本では別々の時間になる。同一時刻を指すようにするにはタイムゾーンの指定が必要になる。

Schemeには日付と時間を扱うSRFIがある。SRFI-19である。多くの場合はこのSRFIで事足りるのだが、局所時間を扱う際にこまることがある。例えば上記の日付をタイムゾーンを気にせず表せない。
;; これはエラー
(make-date 0 0 10 11 4 1 2018 #f)
;; これは常にUTC
(make-date 0 0 10 11 4 1 2018 0)
;; これだと常にUTC+1:00、夏時間どうする?
(make-date 0 0 10 11 4 1 2018 3600)
とりあえず全ての日付はUTC+0にして扱うという手もあるのだが、バッチ処理を日付が変わるときに行うというようなことが面倒になる。また、このSRFIには日付のみ、時間のみを扱う術がない。例えば11時15分という時間と(make-date 0 0 15 11 0 0 0 0)は等価か?あるいは2018年1月4日は(make-date 0 0 0 0 4 1 2018 0)と等価であるかということである。答えはケースバイケースであるとは思うが、多くの場合はNoではないだろうか?

Javaは1.8からjava.timeというパッケージが導入された。これは上記のようなジレンマを解決してくれそうな雰囲気がある(使ったことないので未確認)。Sagittariusにもこれと似たようなものを入れるべきかなぁと考えている。こんな感じでの階層だろうか?
+-------+   +-------+
| date  |   | time  |
+---+---+   +---+---+
    |           |
    +-----+-----+ 
          |
    +-----+-----+   +-------------+
    | date-time |   | zone-offset |
    +-----+-----+   +------+------+
          |                |
          +-------+--------+
                  |
       +----------+----------+
       | offsetted-date-time | <-- SRFI-19 dateと等価
       +---------------------+
時間が常にローカル時間かというのはわからないので、offsetted-timeみたいなのもあっていいかもしれない。timeだと名前が被るから別名にする必要も有りそうだが。

ちょっと考える必要があるが、割と早めに導入したい気持ちもある。

追記
Shiroさんのアイデア


これを使ってSRFI-19を実装するとすると、Calendarは3つ必要になりそう。(たぶん)Gregorian、JulianとModified Julian。内Julian calendarとModified Julian calendarは要らんかもしれんが、あると綺麗っぽい。これを踏まえると以下のようにするといいだろうか?
                  +------------+
                  |  timezone  |
                  +------------+
                  | - Offset   |
                  +------+-----+
+------------+           |
|    date    |           |
+------------+           |
| - timezone |<>---------+                  +------------+
| - calendar |<>------------+               | date-tuple |
+------------+              |               +------------+
                 +----------+----------+    | -(Y,M,D)   |
                 |      calendar       |    +-----+------+
                 +---------------------+          |
                 | - YMD: date-tuple   |<>--------+
                 | - HmS: time-tuple   |<>--------+
                 +---------------------+          |
                                            +-----+------+
                                            | time-tuple |
                                            +------------+
                                            | -(H,m,S)   |
                                            +------------+
calendarは演算手続きの集合にして、こうした方がいいだろうか?
+--------------+                                  +------------+ +------------+
|   calendar   |                                  | date-tuple | | time-tuple |
+--------------+                                  +------------+ +------------+
| - operations |                                  | - (Y,M,D)  | | - (H,m,S)  |
+------+-------+                                  +------+-----+ +------+-----+
       |                                                 |              |
       |             +-------------------+               |              |
       |             |     local-date    |               |              |
       |             +-------------------+               |              |
       |             | - YMD: date-tuple |<>-------------+              |
       +-----------<>| - calender        |                              |
       |             +-------------------+                              |
       |                                                                |
       |             +-------------------+                              |
       |             |     local-time    |                              |
       |             +-------------------+                              |
       |             | - HmS: time-tuple |<>----------------------------+
       +-----------<>| - calendar        |
                     +-------------------+
                              :
                            so on
                              :
どっちもしっくりこない感じがするなぁ。もう少し考える必要がありそうだ。

2018-01-02

謹賀新年

年末年始を病院で過ごしていたのでいわゆる「今年を振り返って」みたいなのが書けなかった。(僕自身が病気ではないです、後は察してください)

昨年はいろいろあって、一年が長かったのか短かったのかよく分からない状態である。少なくとも去年の1月に何をしていたのかは全く思い出せない。引っ越しがあったのでそれに記憶の大部分を取られている気もする。

今年の抱負と目標:
  • ジムに週二で通う
    • 割と毎年言ってるなぁ
  • Scheme関連のブログ記事を週一で書く
    • 習慣にすればなんとか行けるかなぁ
    • 最悪「Sagittariusの中身」とか書きだせば…
  • 今作ってるWebアプリを公開する
    • モチベーションが落ち気味なので宣言しておけば嫌でもやるだろう
あんまり関係ないけど、転職も考えていたりする。完全リモートワークOKならオランダ国外でも問題ないです。