Nim

その朝も早起きしてインターネットしてたら、突然部屋の蛍光灯がチカチカ、そして消えちゃったよ。 そろそろ蛍光灯も寿命か? なんて考えていたら、パソコンの電池がヘタっているから、買ってね、ってご案内が 出てきた。天然UPSもちゃんとメンテしなきゃなんて思っていると、ネットに繋がらなくなった。

もうパソコン止めて、ごみ出しにでも行くか。台所の電気が点かず。これって、ひょっとして停電? 外は薄暗いも、かなたのアパートの防犯灯は点灯してた。停電、おいらの所だけ? それとも、 この集合住宅だけ?

ゴミを棄てに行く時、たまたま隣の人と遭遇。停電してません? ええ、してます。違う地域に 住んでる子供の友人に聞いたら、そちらも停電してるとか。そうすると広い地域で停電だね。

30分ほどして、防災無線から、広域停電してます。復旧の目処は立っていません。交通と火の元には 十分に注意して下さいってのが、流れてきたよ。そうか、朝食を作るのに、焚き火をするんで、 注意しろって御触れなんだな。納得。

おいらはとっくに朝食してるからいいけど、女房はトースターも使えずで困るだろうに。 ガスはプロパンだからいいけど、給湯器は使えんかった。女房は、この時とばかり、 フライパンで、フレンチトーストしてたぞ。

何時復旧するか分からん日でも、子供達は元気に登校してったぞ、と言うと、女房は、こんな日に 給食出るのかしらなんて言う。続けて、町内見回りに行ってきたらとも。

散歩をかねて出陣。信号消えてた。交通整理のおばさんが居たので、給食出るんですかねぇと、 聞くと、ああ、そこまで頭が回りませんでしたと。子供達に聞いてみた。今日はお弁当を持って 行くの? いいえ、違いますだって。 じゃ、今日は半どんだよって、さりげなくデマを流布。

駅に行ったら、真っ暗。駅長さんが出てきた、もうすぐ復旧します、ですって。電車を諦めて、 気動車(ディーゼル車)でも引っ張り出してきたの? それにしたって、信号系が駄目だろうに。 まてよ、昔ながらタブレットと言うかトークンで運用するんかな。 これの技術、プログラミングでクリティカルゾーンを実行する時にも、使われているからなあ。

幹線道路は、おまわりさんが、手信号で交通を捌いていた。ボランティアの人も出てた。 パーキングは、バーが開放されてて、停電時のフェイルセーフ設計が出来てた。 とあるスーパーでは、自家発電のエンジン音が聞こえた。

家に帰ると、女房はメタル電話改め光電話が駄目なんで、携帯で実家に連絡したと言ってた。 携帯のワンセグで、送電線が故障したって言ってた。防災無線は、相変わらず、停電してますとしか、言わない。 情報提供不足だな。

4時間程して復旧。ipadがネットに繋がらなくなった。パスワードが間違ってますだって。 パソコンは繋がる不思議な現象。ルーターにログインして設定を再確認したら、MACアドレスで 接続制限してるも、ipadのそれが洩れてた。設定して、セーブして無かったんだな。

機器は設定した後、一度電源を切り、コールドスタートして確認しておきましょうって言う、 鉄則を忘れていた、つけが回ってきた訳ですよ。それにしても、約2年間、停電もなく動かして きた、電力会社には敬意を払わなくていけないな。

ちなみに、送電線の相間ショートが、運用線とバックアップ線でほぼ同時に起こったとは、 マーフィな法則の再確認になりましたとさ。それにしても、線間が7メートル有るそうだけど、 落雪時のバウンドで、ショートまたは近接放電を起しただろう説が濃厚とか。 ああ、ギャロッピング現象 による停電の見解が出たな。送電線の迂回路が無かったのが致命的だな。インフラ設計は、 インターネットに学ぼうね。

今回の教訓として、パソコンは即シャットダウンしろ、水は出るうちに汲んでおけ。風呂水は、 トイレ水に出来るんで、満タンにしとけ。携帯やトランシーバーと言うか ゼネカバな受信器は、充電を怠らず。

Nim

次は何をやるかねぇ? HAXEなんてのも有るみたいだけど。こいつは オイラーの脳内キューにぶち込んでおいて、先に知ったNimからだな。

所でNimってどゆ意味? 盗む、くすねるってネガティブだなあ。まるでコピペ天国の あの国みたい。まてまて、ポジティブな意味もあるよーん。それはね、素早い、機知に富む とか、敏捷、融通の利くって意味もあるんだってさ。

総本家は、nimです。FreeBSDにも来てるかと思ったら、

[sakae@fb10 /usr/ports/lang/nimrod]$ cat pkg-descr
Nimrod is a statically typed, imperative programming language that tries
to give the programmer ultimate power without compromises on runtime
efficiency.  This means it focuses on compile-time mechanisms in all
their various forms.

Beneath a nice infix/indentation based syntax with a powerful (AST
based, hygienic) macro system lies a semantic model that supports a soft
realtime GC on thread local heaps.  Asynchronous message passing is used
between threads, so no "stop the world" mechanism is necessary.  An
unsafe shared memory heap is also provided for the increased efficiency
that results from that model.

WWW: http://nimrod-code.org/

nimの前身が、0.9.2って事で1年前にリリースされたようで、それ以降のやつは入って いませんでした。nimrodって、聖書に出て来てて、ニムロデ(狩猟の名人)、狩猟家 という意味らしい。

今は、0.10.2って事だから、名前を改めたって訳ね。Webを検索する時は古い名前も 対象にすると良い事があるかも知れないな。

入れ方は、zipファイルを落としてきて展開。中にあるbuild.shを走らせるだけ。数分すると、 bin/nimが出来る。後はPATHを通すだけ。Windowsを見据えて凝った事はしてない。

まてまて、install.shを覗いてみると、

    "/usr/bin")
      bindir=/usr/bin
      configdir=/etc
      libdir=/usr/lib/nim
      docdir=/usr/share/nim/doc
      datadir=/usr/share/nim/data
      ;;

こんな事が書いてあるから、ばらばらに配置も出来るのね。それより興味深いのが、build.sh。 卒倒するような長さですよ。

何が長いって、それぞれのOS用について32Bitと64Bitでソースが分かれてて、 それ用にコンパイル方法が指定してありました。

2年1組は、Linuxの32Bit用、2年2組はLinuxの64Bit用と言った具合。FreeBSDの32Bit用は、 5年1組に入れられていたぞ。FreeBSD6.4の古いものでもちゃんとコンパイル出来たよ。

調子こいて、FreeBSD10にもいれようとした、gcc無いって言われた。build.sh内のgccをccに 変更。これでコンパイル出来た。次はハロワなんだけど、ここでもgccを要求された。しょうが ないので、nim.cfg内で、gccをclangに変更したよ。FreeBSDのデフォのコンパイラはclang だものね。

はろわ

世間並みにハロワしときます。

[sakae@fedora t]$ nim c -r hello.nim
config/nim.cfg(45, 2) Hint: added path: '/home/sakae/.babel/pkgs/' [Path]
config/nim.cfg(46, 2) Hint: added path: '/home/sakae/.nimble/pkgs/' [Path]
Hint: used config file '/home/sakae/nim/config/nim.cfg' [Conf]
Hint: system [Processing]
Hint: hello [Processing]
[Linking]
Hint: operation successful (8755 lines compiled; 4.863 sec total; 9.973MB) [SuccessX]
/home/sakae/t/hello
Hello, Nim!

そして

[sakae@fedora t]$ tree
.
├── hello
├── hello.nim
└── nimcache
    ├── hello.c
    ├── hello.o
    ├── system.c
    └── system.o

nim語で書いたのが、C語に変換されて、それがgccでコンパイルされるとな。こういう方式は、 昔Schemeでやったな。えと、chickenとかGambit Scheme。あいつにらには、インタープリタとコンパイラが 付属してたけど、nimはどうよ?

[sakae@fedora t]$ nim i
config/nim.cfg(45, 2) Hint: added path: '/home/sakae/.babel/pkgs/' [Path]
config/nim.cfg(46, 2) Hint: added path: '/home/sakae/.nimble/pkgs/' [Path]
Hint: used config file '/home/sakae/nim/config/nim.cfg' [Conf]
Hint: system [Processing]
lib/system/ansi_c.nim(38, 4) Error: cannot 'importc' variable at compile time
lib/system/ansi_c.nim(39, 4) Error: cannot 'importc' variable at compile time
lib/system/ansi_c.nim(40, 4) Error: cannot 'importc' variable at compile time
lib/system/ansi_c.nim(151, 10) Error: cannot 'importc' variable at compile time
lib/system.nim(2281, 6) Error: cannot 'importc' variable at compile time
lib/system.nim(2283, 6) Error: cannot 'importc' variable at compile time
lib/system.nim(2285, 6) Error: cannot 'importc' variable at compile time
Hint: stdin [Processing]
>>> echo("Hello NIM")
Hello NIM
>>>

実用になるかどうかは、分からんけどPythonみたいな雰囲気だな。

ついでに、nimのオプションを見とくと

[sakae@fedora t]$ nim  --advanced
Nim Compiler Version 0.10.2 (2014-12-29) [Linux: i386]
Copyright (c) 2006-2014 by Andreas Rumpf
Advanced commands:
  compileToC, cc          compile project with C code generator
  compileToCpp, cpp       compile project to C++ code
  compileToOC, objc       compile project to Objective C code
  js                      compile project to Javascript
  rst2html                convert a reStructuredText file to HTML
  rst2tex                 convert a reStructuredText file to TeX
  jsondoc                 extract the documentation to a json file
  buildIndex              build an index for the whole documentation
   :

みんな大好きjs語にも変換出来るのね。

[sakae@fedora t]$ nim js hello.nim

これで、キャッシュの中に、hello.jsが出来上がった。オイラーは別に嬉しくも何とも ないけどね。まあ、時代が時代ですから、ブラウザーと言う仮想CPUのアセンブラーを 吐き出すのが今風なんでしょうな。

nimを書く為のemacsの設定

って、nimを書く為のvimの設定に倣った訳ではありません。あちらは、quickrun一発らしい ですけど。。。

nim-modeと、ac-nimも入れて補完完了。 設定は、

;; nim
(setq auto-indent-on-visit-file t) ;; If you want auto-indent on for files
(require 'auto-indent-mode)
(add-to-list 'auto-indent-multiple-indent-modes 'nim-mode)
(eval-after-load 'nim-mode
   '(add-hook 'nim-mode-hook 'ac-nim-enable))

後はquickrun対抗品。なんでもanyとかあるらしいけど、使いこなせないだろうから、 シンプルに、godocからの連句を作りました。nim-mode.elの最後にでも、コピペ。

(defun nim-run (query)
  "nim run on buffer *nim-run-output*"
  (interactive (list (buffer-file-name)))
  (unless (string= query "")
    (save-buffer)
    (start-process-shell-command "nim-run" "*nim-run-output*"
         (concat "nim c --hints:off -r " (buffer-file-name) " "
                                  (read-from-minibuffer "args: ")))
    (switch-to-buffer "*nim-run-output*")
    nil))

(defun nim-clear-screen ()
  (interactive)
  (erase-buffer))

(define-key nim-mode-map (kbd "\C-c\C-r") 'nim-run)

nimble

nimbleって、nim用のパッケージ・マネージャだそうです。

インストールする時、libcryptoとlibsslが見つからんと言われたんで、それ用にリンクして 完了。

[sakae@fedora ~]$ nimble update
Downloading package list from https://github.com/nim-lang/packages/raw/master/packages.json
Done.
[sakae@fedora ~]$ nimble list
   :
csv:
  url:         git://github.com/achesak/nim-csv (git)
  tags:        csv, parsing, stringify, library
  description: Library for parsing, stringifying, reading, and writing CSV (comma separated value) files
  license:     MIT
  website:     https://github.com/achesak/nim-csv
    :

136個、登録されてた。Rustに比べてどうよ。物は試しにインストールしてみる。

[sakae@fedora ~]$ nimble install csv
Downloading csv into /tmp/nimble_2133/csv using git...
Initialized empty Git repository in /tmp/nimble_2133/csv/.git/
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), done.
From git://github.com/achesak/nim-csv
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> origin/master
HEAD is now at 6a982da Fixed inconsistent behavior and added skipBlankLast option. Fixes part of #1.
Switched to a new branch 'origin/master'
Installing csv-0.1.0
/tmp/nimble_2133/csv/documentation.html -> /home/sakae/.nimble/pkgs/csv-0.1.0/documentation.html
/tmp/nimble_2133/csv/csv.nimble -> /home/sakae/.nimble/pkgs/csv-0.1.0/csv.nimble
/tmp/nimble_2133/csv/csv.nim -> /home/sakae/.nimble/pkgs/csv-0.1.0/csv.nim
/tmp/nimble_2133/csv/README -> /home/sakae/.nimble/pkgs/csv-0.1.0/README
/tmp/nimble_2133/csv/LICENSE -> /home/sakae/.nimble/pkgs/csv-0.1.0/LICENSE
/tmp/nimble_2133/csv/csv.nimble -> /home/sakae/.nimble/pkgs/csv-0.1.0/csv.nimble
csv installed successfully.

nimbleにはinitコマンドなんてのが装備されてて、プロジェクトの開始時に使ってね、なんて 説明が有ったけど、具体的にはどうすればいいの? その答えが、 How I start Nimなんて所に載ってた。

nimbleで提供されるモジュールを作って、Nimを盛り上げようぜって方法の説明だった。 今なら提供数も少ないので、いろいろやると良いかも。gemやcpanみたいになっちゃっちゃ、 埋もれてしまいますから。

なにはともあれ、Nimへのとっかかりとしては、非常に良く出来たアーティクル。是非、手を 動かしてみたいぞ。せっかちな人は、 Learn X in Y minutes where X=Nimがよい。

nimbleから、strfmtを入れておくと、

import strfmt

echo "Hello {} number {:04.1f}".fmt("World", 6.0)

こんな風に、フォーマット出力出来るようになるぞ。是非入れとけ。

スクリプトみたい

上記のフォーマット出力をnimで実行して、結果が得られるまでの時間を測ってみる。

[sakae@fedora z]$ time nim c -r hoge.nim
config/nim.cfg(45, 2) Hint: added path: '/home/sakae/.babel/pkgs/' [Path]
config/nim.cfg(46, 2) Hint: added path: '/home/sakae/.nimble/pkgs/docopt-0.1.0' [Path]
config/nim.cfg(46, 2) Hint: added path: '/home/sakae/.nimble/pkgs/strfmt-0.5.5' [Path]
config/nim.cfg(46, 2) Hint: added path: '/home/sakae/.nimble/pkgs/csv-0.1.0' [Path]
config/nim.cfg(46, 2) Hint: added path: '/home/sakae/.nimble/pkgs/nimble-0.6.0' [Path]
config/nim.cfg(46, 2) Hint: added path: '/home/sakae/.nimble/pkgs/' [Path]
Hint: used config file '/home/sakae/nim/config/nim.cfg' [Conf]
Hint: system [Processing]
Hint: hoge [Processing]
Hint: strfmt [Processing]
Hint: macros [Processing]
Hint: strutils [Processing]
Hint: parseutils [Processing]
Hint: unicode [Processing]
Hint: math [Processing]
Hint: times [Processing]
Hint: fenv [Processing]
Hint: unsigned [Processing]
Hint: pegs [Processing]
Hint: streams [Processing]
[Linking]
Hint: operation successful (17962 lines compiled; 4.507 sec total; 24.250MB) [SuccessX]
/home/sakae/z/hoge
Hello World number 06.0

real    0m4.540s
user    0m1.248s
sys     0m3.171s

strfmtは裏でいろいろなソースに展開してんのね。結構時間がかかった。二回目からは、 必要な部分だけコンパイルされるので、早くなる。

  :
/home/sakae/z/hoge
Hello World number 006.54

real    0m1.059s
user    0m0.503s
sys     0m0.505s

もっと早くならないの? そんなせっかちな人は、Tiny C Compilerを gccの代わりに使えばよい。それ用のドライバーと言うか、スクリプターは、次のようにする。

[sakae@fedora z]$ cat nimhs
#!/bin/sh
nim --cc:tcc --verbosity:0 -d:release -r c $*

これを使って、hoge.nimを、スクリプトのように実行してみる。

[sakae@fedora z]$ time ./nimhs hoge.nim
Hello World number 006.54

real    0m0.972s
user    0m0.219s
sys     0m0.727s

二回目の実行は

[sakae@fedora z]$ time ./nimhs hoge.nim
Hello World number 012.35

real    0m0.782s
user    0m0.285s
sys     0m0.480s

まるで、型落ちしたパソコンで、Pythonスクリプトを実行してるみたい。

debug

情けない事に、もしもの為のdebug方法を見とく。例にそのものずばりが出てた。

[sakae@fedora examples]$ cat -n debugging.nim
     1  # Simple program to test the debugger
     2  # compile with --debugger:on
     3
     4  proc someComp(x, y: int): int =
     5    let a = x+y
     6    if a > 7:
     7      let b = a*90
     8      {.breakpoint.}
     9      result = b
    10    {.breakpoint.}
    11
    12  proc pp() =
    13    var aa = 45
    14    var bb = "abcdef"
    15    echo someComp(23, 45)
    16
    17  pp()

フラグを付けてコンパイルしたやつを走らせると、debuggerに落ちてくれるんだろう。多分。

*** endb| examples/debugging.nim(17) debugging ***
*** endb| >>h
*** endb| list of commands (see the manual for further help):
              GENERAL
h, help                 display this help message
q, quit                 quit the debugger and the program
<ENTER>                 repeat the previous debugger command
              EXECUTING
s, step                 single step, stepping into routine calls
n, next                 single step, without stepping into routine calls
f, skipcurrent          continue execution until the current routine finishes
c, continue, r, run     continue execution until the next breakpoint
i, ignore               continue execution, ignore all breakpoints
              BREAKPOINTS
b, break [fromline [toline]] [file]
                        set a new breakpoint for line and file
                        if line or file are omitted the current one is used
breakpoints             display the entire breakpoint list
toggle fromline [file]  enable or disable a breakpoint
filenames               list all valid filenames
              DATA DISPLAY
e, eval <expr>          evaluate the expression <expr>
o, out <file> <expr>    evaluate <expr> and write it to <file>
w, where                display the current execution point
stackframe [file]       display current stack frame [and write it to file]
u, up                   go up in the call stack
d, down                 go down in the call stack
bt, backtrace           display the entire call stack
l, locals               display available local variables
g, globals              display available global variables
maxdisplay <integer>    set the display's recursion maximum
***
*** endb| >>c
*** endb| reached examples/debugging.nim(8) someComp ***
*** endb| >>bt
examples/debugging.nim(17) debugging
examples/debugging.nim(15) pp
examples/debugging.nim(8) someComp
 :
*** endb| >>l
*** endb| Frame (4 slots):
result = 6120
x = 23
y = 45
a = 68
*** endb| >>q

これだけ使えれば、十分かな。詳しくは、 Embedded Nim Debugger (ENDB) User Guideを参照の事。 ついでに、プロファイラーもって事なら、 Embedded Stack Trace Profiler (ESTP) User Guide

まてまて gdbでも試験しておくか。

[sakae@fedora z]$ cat -n fuga.nim
     1  var x = 12345
     2  for i in 1 .. 3:
     3    echo(i)
     4  echo(x)
[sakae@fedora z]$ nim --lineDir:on --debuginfo c fuga.nim
[sakae@fedora z]$ gdb -q ./fuga
Reading symbols from ./fuga...done.
(gdb) b fuga.nim:3
Breakpoint 1 at 0x8061431: file /home/sakae/z/fuga.nim, line 3.
(gdb) run
Starting program: /home/sakae/z/fuga
Missing separate debuginfos, use: debuginfo-install glibc-2.20-7.fc21.i686

Breakpoint 1, fugaInit () at /home/sakae/z/fuga.nim:3
3         echo(i)
(gdb) c
Continuing.
1

Breakpoint 1, fugaInit () at /home/sakae/z/fuga.nim:3
3         echo(i)
(gdb) p i_88018
$1 = 2
(gdb) p x_
x_88004     x_getpostn  x_putbytes  x_putlong
x_destroy   x_inline    x_putint32  x_setpostn
(gdb) p x_88004
$2 = 12345

変数名は、gdbに推測させる事。どうやら、変数名_12345 みたいのが、それみたい。

こうしてdebugが完了したら、リリース。早く動かす方法と、サイズを小さくする方法が トリックとして挙げられていたぞ。

Q.  Which option to use for the fastest executable?
A.  For the standard configuration file, ``-d:release`` does the trick.

Q.  Which option to use for the smallest executable?
A.  For the standard configuration file, ``-d:quick --opt:size`` does the trick.

一応、確認してみるか。題材は前回のWebに出てた、各種言語のベンチ

[sakae@fedora z]$ cat fib.nim
proc fib(n: int): int =
  if n < 2:
    return n
  return fib(n - 2) + fib(n - 1)

echo(fib(42))

普通にコンパイルすると

[sakae@fedora z]$ time ./fib
267914296

real    0m37.644s
user    0m37.331s
sys     0m0.187s

今度は、スピード優先でコンパイル

[sakae@fedora z]$ nim c -d:release fib.nim
[sakae@fedora z]$ time ./fib
267914296

real    0m2.049s
user    0m1.999s
sys     0m0.043s

爆速になりましたと、メディアが好む宣伝文句を一応、の賜わってみます。

そして次は、小さくなーれの呪文を唱えてみます。

[sakae@fedora z]$ nim c -d:quick --opt:size fib.nim
[sakae@fedora z]$ time ./fib
267914296

real    0m8.599s
user    0m8.552s
sys     0m0.014s
[sakae@fedora z]$ size n.fib fib
   text    data     bss     dec     hex filename
 125440     424    5716  131580   201fc n.fib
  14659     308    4000   18967    4a17 fib

元が小さいので(go比ですけど)、余り嬉しくないな。

訳あり

爆速の理由は何処にあったんでしょうか? と、これまたTV屋が好むキャッチャー句を 並べてみます。

手がかりは、オプションで指定した、-d: に有りだな。releaseを付けると、各種チェックが 省略された上に、最適化が行われるとな。最適化で何十倍も速くなるってのは、ちと信じられないので、 各種チェック(と言う余計なお世話、もとえ、有り難い、小言)が、幅を効かせているんだろうな。

nim.cfgにこんな事が書いてあった。@ifは#ifの代わりね。#はコメント記号として使っているから。

@if release or quick:
  obj_checks:off
  field_checks:off
  range_checks:off
  bound_checks:off
  overflow_checks:off
  assertions:off
  stacktrace:off
  linetrace:off
  debugger:off
  line_dir:off
  dead_code_elim:on
@end

@if release:
  opt:speed
@end

最適化を施して生成されたfib.cは、

N_NIMCALL(NI, fib_88003)(NI n) {
        NI result;
        NI LOC5;
        NI LOC6;
        result = 0;
        {
                if (!(n < 2)) goto LA3;
                result = n;
                goto BeforeRet;
        }
        LA3: ;
        LOC5 = 0;
        LOC5 = fib_88003((NI32)(n - 2));
        LOC6 = 0;
        LOC6 = fib_88003((NI32)(n - 1));
        result = (NI32)(LOC5 + LOC6);
        goto BeforeRet;
        BeforeRet: ;
        return result;
}

こんな具合に、すっきりサッパリしてるのに対して、チェックが入って安全側に振って あるやつは、ここに表示し切れないぐらい、いろいろな事をやってたぞ。

結局、十分に虫取りしたら、監視コードは削除した上で、野に放つって訳だな。 これって、子供が親の保護を受けて成長し、自立してくのと一緒の事だな。これから、 社会の荒波に向かって船出するみなさん、頑張ってくらはい!

Nim on Rosetta Code

Nimなページに、Nim on Rosetta Code なんてのが出てた。

ロゼッタ・ストーン って古代の記録なのね。解読したものが、日本語になってた。 ロゼッタストーン/日本語訳とその解説

人間が喋る言語は、数千あるそうだけど、どんどん話者がいなくなって、衰退していって るそうな。

コンピュータ言語は、次々に生まれて、死ぬのも速い。記録を残しておこうってんで、 コンピュータ言語用のロゼッタストーンが用意された。Nimも将来に備えて、登録して あるんだな。

遺跡言語にならないように、頑張って話者を増やしてください。オイラーも応援しますよ。