Nim (5)

『ご当地電力はじめました!』(岩波ジュニア新書)なんて本を読んだ。小供向けの本を取り上げ ちゃった訳だけど、3月に起きた停電がきっかけだったりします。

現在の日本は、10大電力会社による独占供給体制になっている。戦前は、ご当地それぞれに 地元の富豪やら組合が小規模な電力会社を作って、地産地消のまかないをやってたらしい。

それを、国の主導で独占供給体制に持って行った。電気エネルギーは、国の根幹に関わる 事、安定に末永く供給するのが、目的。

黒部ダムの水力発電はその象徴ですなあ。あれだけの大工事を小企業が出来る訳がない。 火力発電もしかり。極めつけは、田舎に作った原子力発電所。東京湾の回りには、ごまんと 火力発電所があるくせに、何故、原子力発電所だけ、田舎に作るの?

安全と言うなら、消費地の近くに作るのが理にかなっているのに。それはね、地方を豊かに するために、原子力、交付金を堂々と支給するため。裏の理由は、利権だったりします。

で、震災でメッキが剥がれてしまった。何で、大都会で消費する電力を田舎に作らせる。 使う所で、作って、それをそこで消費する。そうだよなあ。それが賢い。 余ったやつは、売ってあげよう。

トラックに積んで発送すればいいの? 発電 送電 配電は独占されてる。売ろうとすると 送電網を自分で作って、配電も自分でやる? 地方の小会社では無理。

送電網を借りよう。配電もお願いしよう。その借用料はどのくらい? 正確には、借用料の 事を託送料金と言うそうだけど、独占企業のいやいや度が如実に反映されてて、アメリカの 5倍もするらしい。10社平均で、3.96円/Kwとか。

いやいや理由は、天気に左右される不安定な電力は、沢山送電網に流せないって事らしいです。 NTTも、ファイバー網を貸したくなくて、いろいろ妨害作戦をやってたなあ。あれと同じ構図が。

オイラーが考えるに、余った電気は水素に変換して貯蔵しておけば良い。夏の最盛期には、 沢山取れるからね。産地から、夕取れ電力とか言って、農家のおばちゃんが軽トラで都会へ 売りに行ったりして。

太陽光発電と言うと、地面に太陽電池を展開する事になるので、土地が死んでしまう。 そこで、太陽光の共有ってのが考えられているそうな。作物の中には余り太陽光が無くても 育つ、菜種みたいなのがあるそうな。電池を細長くして、作物と光を共有する事も実験が 始まっているとか。

でも、一番は、屋根だね。全国でも晴天率が高い、長野県飯田市では、地元の有志が 公共施設の屋根を使って発電するように行政に働きかけた。そういうの、建物の目的外 使用って事で、最初はけんもほろろに断られたたしい。が、新市長は、そういうのに 理解があり、許可をくれたとか。それが発端となって、太陽光発電が増殖してるとか。

補助金出します。予算を使い終わったら、それで終了って言う行政より、そういう支援の 方が、ずっとよい成果が出るとの事。

会津地方は山の中。昔から自給自足を旨としてきた。電気だってそうすべ。醸造所の人やら 農協の人やら、有志が出資して、水力発電所を作ったとか。小田原市では、戦前に使って いた、水力発電所を掘り起こして復活させるグループが活動してるとか。

送電網の分離と言うか開放が予定されるようだけど、情報が通り光網は、一足お先に 開放されたようだ。使いてぇって名乗りを挙げたのは、しのぎを削る電話会社。

先日も、光モデムを交換したら回線代がお安くなりますから、契約しませんかと電話が かかってきたぞ。TV電話じゃなかったので、相手があの装束をまとっていたかは確認できず。

交換して頂いた特典として、スマホやガラケーとセットで更にお安くなりますってさ。 で、ちと確認させて下さいって、取調べがあった。パソコン何台つないでますか? 光TVに 加入してますか?

ユニ・キャストだけじゃなくて、マルチ・キャスト対応のルーターが必要かの下調べだな。 あわよくば、自社と提携してるコンテンツ屋に情報流したいんか。 適当に答えて、お引取り願ったぞ。

lisp-1 or 2 ?

nimは、関数名が同じでも、引数の型や個数が異なったものは、別の扱いをしてくれた。 それは最初混乱するけど、慣れれば、快適。

じゃ、変数名と関数名は同じでも許される? Lisp界隈では、これを区別しないLisp-1派 (Scheme族)と、区別するLisp-2派(CommonLisp族)に分かれて、熾烈な論争を展開してる。

Nimはどっち派? オイラー予想では、Lisp-1派と思うんだけど。。。夜眠れなくなるといけないので、 ここらあたりできっちり確認しておこう。きっとC語に倣っているのでしょう。尤も、C語で 確認した事は無いけど。

var my = "hello var"
proc my() : string = return "hello proc"
echo my

これを走らせると

l12.nim(2, 5) Error: redefinition of 'my'

Uum... 変数名と関数名に同じものは許されないのか。Lisp-1 だな。

じゃ、関数問題に戻って、こういうのはどう?

proc my() : string = return "noARG"
proc my() : int    = return 123
proc my(w:string) : string = return "hello " & w
proc my(i:int)    : string = return $i
echo my(123)
echo my()

引数無しで、返す型が違うやつ

l12.nim(6, 7) Error: ambiguous call; both l12.my(): string and l12.my(): int match for: ()

どっちにするねん? はっきりしいや!! 定義は出来るけど、使えない奴が出来ちゃったって事ですな。 どっちつかずの6行目を削除すれば、涼しい顔して123が表示されます。 後、こんなのも思い付いた。

var aaa : string = my()

これなら、左側で文字列よってきっちり要求してるんで、それに合致するmyを選んでくれるかな。 でも、駄目だった。どっちにすんねんエラーが発生。お隣のHaskell業界はいかがかな。 これぐらいやっとけば、場合を尽くした事になるかな?

場合と言えば、caseはちゃんと場合を尽くしているかチェックしてる? 勿論こういうのは、うろ覚えのハスクル君とかオッカムさん対抗ですけど。

const x = 1
case x:
    of 1: echo "one"
    of 2: echo "two"

幼児用の数字を英語に直すやつ。

case.nim(2, 0) Error: not all cases are covered

ちゃんと見てるね。穴を塞ぐには、最後の所にelse: を入れましょう。でも、塞がないと 実行バイナリーが出来ないかと思うと、

const x = "f"
case x:
    of "f": echo "one"
    of "s": echo "two"

こういうのは、黄色信号が灯って、注意進行出来たりします。

case.nim(2, 0) Warning: use 'else: discard'; non-ordinal case without 'else' is deprecated [Deprecated]
[Linking]
/home/sakae/nim/t/case
one

事故責任。有り難く、仰せに従った方が良さそうですよ。

Nimの追い駆け

Nimの処女地ウブにも入れてやろうと思った。普通に入れたんじゃつまらないので、入れた後に 追い駆け出来るようにしとく。

git clone -b master git://github.com/Araq/Nim.git
cd Nim
git clone -b master --depth 1 git://github.com/nim-lang/csources
cd csources && sh build.sh
cd ..
bin/nim c  -d:withUpdate koch
./koch boot -d:release

ふむ、最初は本体とライブラリーを取ってくるとな。次に種となるnimのC語を取ってくる。 ここは、普通それぞれのOS用のバイナリーで供給されるんだけど、ソース出すから、好きに しろか。

出来上がった種nimを使って、kochって言うnim開発用の料理人を仕立て上げるとな。この時に、 withUpdateを指定しとくと、gitのアップデートが簡単に出来る。kochってのは、ドイツ語。 意味はcookね。

で、コックさんに、nim語から、nimのバイナリーを作ってもらうとな。後は、定常業務として、 koch updateしてから、koch boot してけば、追い駆けになる。残念ながら、余りupdate されてないけどね。だったら、master ブランチより devel ブランチにしたら? 山有り谷有りで、 楽しめる事でしょう。

git logしたら、日本人とおぼしき方が2人いた。頑張ってください。

Nimの手本

nimの書き方は、Webに若干ルールとして載ってる。インデントは2文字分ってのがデフォの ようである。説明的な名前を横幅80文字の中に収めようとすると、なるべく詰めたいのは 理解できますけど。オイラーは、インデント幅4文字です。

なにか適当な手本って事で、kochを見てく。

proc exe(f: string): string = return addFileExt(f, ExeExt)

proc findNim(): string =
  var nim = "nim".exe
  result = "bin" / nim
  if existsFile(result): return

exeなんて言う凄い名前の関数を定義してるんで、"nim".exe なんて言う洒落っ気の ある事が出来るのね。"bin" / nim なんて、もう駄洒落もいい所。無骨なドイツ人が こんなコードを書くんですかね?

proc exec(cmd: string) =
  echo(cmd)
  if execShellCmd(cmd) != 0: quit("FAILURE")
 
  exec("$# cc $# $#" % [findNim(), args, toolname])

pythonみたいな割り当てと言うか分配が、strutilsの % で出来るのね。

このkochは、最後にmainを定義して、それを呼ぶってスタイルかと思ったら、違った。 parseoptを呼び出して、コマンドラインを分解。それを元に各コマンドにディスパッチする スタイルになってた。

もう一つ流派が有って、Pythonのモジュールの最後で、テストコードのごとく、そのモジュールを 呼び出す方法。勿論、nimにも備わってる。以下は、examplesの中の例。

when isMainModule:
  var port = 80

このモジュールを単独で呼び出すと、isMainModuleがtrueになって、以下が実行されてくってやつね。 コンパイラーマジックだよ感謝しなって、systemの所に書いてあったぞ。

nim-gmp

今までnimを堪能してきた訳だが、goに有るmath/big相当が、nimには無くて、ちと残念と 思ってた。BiggstIntをBiggerIntと早とちりしたしね。

そんな折、nimbleで新しいやつが投行されてるか調べていたら、 nim-gmpなんてのに出会った。これって、 多倍長演算をnimから出来るようにしたものだよな。光が見えてきましたよ。

とりかかる前に、多倍長演算の業界を、 多倍長計算ノート で、予習したよ。そして、いよいよ、 多倍長整数演算ライブラリ (GNU MP) とか、Windows用にGMPのインストールから解説した GMP の使い方を見て、使えるじゃんとなった訳。

以前goでやったのをnim語に書き換えた。必ず登場するfactね。

import gmp
import gmp/utils

let one: mpz = 1   # convert int to mpz_t
var r = init_mpz() # initialized to zero
var n = init_mpz("100",10) # construct from string (base: 10)

mpz_set(r, n)
while true:
    mpz_sub(n, n, one)
    mpz_mul(r, r, n)
    if mpz_cmp(n, one) == 0:
        break

echo r

合ってるかな?

[sakae@fedora gmp]$ nimhs fact.nim
/home/sakae/.nimble/pkgs/nim-gmp-0.2.1/gmp/pure.nim(67, 2) Hint: 'GMP_RAND_ALG_LC' is declared but not used [XDeclaredButNotUsed]
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

合ってた。goのmath/bigを見た時、使い方に違和感が有ったんだけど、これはきっとGMPに 合わせた設計だったのね。goの営業戦略が何となく透けて見えますな。

折角良い物が手に入ったので、ウブにも持って行ってやろう。その前に、nimbleのマネージャが どうやって管理してるか見ておく。

[sakae@fedora pkgs]$ tree nim-gmp-0.2.1
nim-gmp-0.2.1
├── gmp
│   ├── extratypes.nim
│   ├── header.nim
│   ├── pure.nim
│   └── utils.nim
├── gmp.nim
├── gmp.nimble
└── nimblemeta.json

ふむ、このまま持って行って、libの下にでも置けば動きそう。それようのwrappers置き場なんてのが 出来ているので、そこに置く事にした。そして、ライブラリィパスに登録しておく。 config/nim.cfgに書いておけばOK。

nimがどう認識するかは、

sakae@uB:~$ nim dump
-- list of currently defined symbols --
nimunion
nimcomputedgoto
  :
-- end of list --
/home/sakae/Nim/lib/pure/unidecode
/home/sakae/Nim/lib/js
   :
/home/sakae/Nim/lib/wrappers/nim-gmp
   :

こんな風にして確認出来る。定義済みシンボルもここで確認出来るので、ソースを読んでる時に 出て来る、when defined(linux) なんてのと付き合わせるのに便利。

sakae@uB:~/src/nim-dev$ ./fact
could not load: libgmp.so

エラーだよ。fedoraの時も同様エラーが出て、強権(rootになって)発動させて、リンク作った。 もし、あんたが弱い人なら、どうする?

sakae@uB:~/src/nim-dev$ locate libgmp.so
/usr/lib/i386-linux-gnu/libgmp.so.10
/usr/lib/i386-linux-gnu/libgmp.so.10.1.3
/usr/lib/i386-linux-gnu/openssl-1.0.0/engines/libgmp.so

libgmp.so.10が有るので、gmp/pure.nimの中のconstで定義してるのを変更すればOK。 変更後、再コンパイルして実行。ちゃんと動いた。おめでとう。gmp.hが無くても動くように 作ってある所が偉いぞ。(上から目線ですな)

gmpのソースを見てたら、mpz用の == が定義されてたので、サンプルで書いたやつは、プチ 簡単になるな。

こちらを見ると、RSA暗号の実装にも使えるようで、なかなかよさげ。 GMP Library 関数メモ for RSA そして、 RSA 暗号がようやく分かった気がしたのでまとめてみる 方や、どこかでお会いした、 Masami Hagiya 先生も、暗号大好き。

調子こいて、FreeBSDでも動くかと試してみたら、無事に動いた。それはそれで良い事なのだけど、一つの疑問が。

[sakae@fb10 ~/t]$ ldd ./fact
./fact:
        libm.so.5 => /lib/libm.so.5 (0x2806e000)
        libc.so.7 => /lib/libc.so.7 (0x28094000)

得意のどんなライブラリィ使ってるので、gmp.soが出てこない。どういう仕組みでgmp.soを 使ってる? nimが吐き出したC語を調べるか。

[sakae@fb10 ~/t/nimcache]$ grep gmp.so *c
gmp.c:STRING_LITERAL(TMP18, "libgmp.so.10", 12);
gmp.c:STRING_LITERAL(TMP19, "libgmp.so.10", 12);
[sakae@fb10 ~/t/nimcache]$ grep TMP18 gmp.c
STRING_LITERAL(TMP18, "libgmp.so.10", 12);
if (!((TMP17 = nimLoadLibrary((NimStringDesc*) &TMP18))
[sakae@fb10 ~/t/nimcache]$ grep TMP19 gmp.c
STRING_LITERAL(TMP19, "libgmp.so.10", 12);
)) nimLoadLibraryError((NimStringDesc*) &TMP19);

ふむ、nimLoadLibraryを使ってるとな。で、それが何処にあったかと言うと、system.cの中。 とすると、Nimのlib/system/の所で探せばいいのか。探したら、dyncalls.nimの中に有った。

  proc dlopen(path: cstring, mode: int): TLibHandle {.
      importc, header: "<dlfcn.h>".}
  proc nimLoadLibrary(path: string): TLibHandle =
    result = dlopen(path, RTLD_NOW)

公開されていないって事は、素人さんを後悔させないためだな。これらはposixさんの時って事なんだけど、他には windowsとmac用の吸収コードが存在してた。

念のため、manしとくと

NAME
     dlopen, fdlopen, dlsym, dlfunc, dlerror, dlclose ? programmatic interface
     to the dynamic linker

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <dlfcn.h>

     void *
     dlopen(const char *path, int mode);
       :

これで納得しました。プログラム中からダイナミックリンカーを呼んで、dlsymとかを使い、 目当ての関数の位置を探り出して、使うって事なのね。

nim doc

gmpのソースに限らず、長いやつをemacsで覗くのは、ちと苦痛になる事が有る。 そんな時は、Nim DocGen Toolsを使って、htmlに してしまうのが楽だ。

w3mだと見栄えはぱっとしないけど、普通のブラウザなら、左側にインデックスが出てきてて、 そこを眺めてクリックすればよい。

haskellで言う所の、シグネチャが簡単に参照出来る。そして運が良ければ、使い方とかの 実例が得られる事があるぞ。

但し、このツールで作られるhtmlには、モジュールから公開してるものしか取り上げて 貰えない。自分が野良で作ったコードなんてのは、公開する関数とかって概念が無いので、 幾ら、nim doc hoge.nim とかしても、列挙されないので注意。

公開は、関数名の後ろにスターマークを付けるだけ。これで後悔しないぞ。 ああ、コメントは、シャープサイン2個ね、かっこよく、シャープで枠を作ると、怒られるので注意。

libの直下にある、system.nimとか、よく使いそうなものをまとめてinportしてくれる、preludeあたりを 押さえておけば、当面は困らないな。ちなみに、preludeは、

import os, strutils, times, parseutils, parseopt, hashes, tables, sets

こんな風になってたぞ。

c2nim

nim-gmpを入れた時、header.nimってのが付いてきた。これの相当品がpure.nimになるんだけど、 このheader.nimって、ctonimってツールを使って 作り出したもの?

面白そうなので使ってみたい。

[sakae@fedora ~]$ nimble search c2nim
c2nim:
  url:         git://github.com/nim-lang/c2nim (git)
  tags:        app, binary, tool, header, C, nimrod
  description: c2nim is a tool to translate Ansi C code to Nimrod.
  license:     MIT
  website:     https://github.com/nim-lang/c2nim

ふむ、便利ツールの扱いか。

[sakae@fedora ~]$ nimble install c2nim
Downloading c2nim into /tmp/nimble_1223/c2nim using git...
Initialized empty Git repository in /tmp/nimble_1223/c2nim/.git/
remote: Counting objects: 38, done.
remote: Compressing objects: 100% (34/34), done.
remote: Total 38 (delta 0), reused 18 (delta 0), pack-reused 0
Unpacking objects: 100% (38/38), done.
From git://github.com/nim-lang/c2nim
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> origin/master
HEAD is now at 97ab67e Merge branch 'master' of https://github.com/nimrod-code/c2nim
Switched to a new branch 'origin/master'
Unsatisfied dependency: nim (>= 0.10.3)

やな予感。haskellのcabalみたいにならないでね。安定版は無いのか。

nimble領域には入らなかったけど、/tmpの下にはソースが鎮座してたので、だめ元でコンパイルしてみる。

[sakae@fedora c2nim]$ nim c c2nim.nim
   :
c2nim.nim(11, 41) Error: cannot open 'compiler/llstream'

importの所でモジュールが無いって怒られました。何やら、コンパイラーがらみの便利モジュールが 色々追加されているようです。

しょうがないので、ウブにNimの開発版を入れた。そしてc2nimをコンパイルしようとしたら、 やはり上と同じエラー。nimのパッケージマネージャ、nimbleを入れないと駄目?

待て待て、まだ手は有るだろうに。libの中から、../compilerにリンクを貼り、nim.cfgに一行追加。

path="$lib/compiler"

最初、リンクを貼らないで、pathに直接/home/sakae/Nim/compilerと設定したけど、有効に ならなかった。libは、あくまでlibを基点にしないと駄目みたい。

試運転って事で、超簡単なヘッダーファイルを作った。

sakae@uB:~/c2nim/doc$ cat hoge.h
#define abc 123
#define xyz 789

これをnim語に変換してみる。

sakae@uB:~/c2nim/doc$ c2nim hoge.h
Hint: operation successful (4 lines compiled; 0 sec total; 516.528KB; ) [SuccessX]
sakae@uB:~/c2nim/doc$ cat hoge.nim
const
  abc* = 123
  xyz* = 789

4行コンパイルしましたって、2Pass方式なの? それはそうと、ちゃんと変換されましたね。 オイラーが考えても、constになるな。以前、C語をgo語に変換した時も、こういうdefineは、定数に 脳内変換したよ。

定数名に米印が付いているのは、だれでもアクセス可能だよマークだ。他のものを変換しても 、みんなexportされるんだろうね。

次は、いきなりgmp.hを変換してみるかな。勿論ウブを筆頭にlinux族は、lib有れどinclude無しって言う、 変なポリシーが浸透しているので、入っていない。取ってくるのはやぶさかでないけど、これ みよがしに、FreeBSDから借用してきました。何せ、FreeBSDは正統派ですから、libとincludeは、 必ずセットになっています。

sakae@uB:~/c2nim/doc$ c2nim gmp.h
home/sakae/c2nim/doc/gmp.h(207, 68) Warning: comment '# Linear congruential.  ' ignored [CommentXIgnored]
home/sakae/c2nim/doc/gmp.h(248, 27) Error: did not expect ##

どんな所で引っかかったの?

   247  #ifndef __MPN
   248  #define __MPN(x) __gmpn_##x
   249  #endif

これって、GNUの拡張で、シャープ2つは、文字列の結合なのね。c2nimの守備範囲は、ANSI Cだから、 エラー検出したって事ね。ここをコメントにして再度変換すると

sakae@uB:~/c2nim/doc$ c2nim --debug gmp.h
  :
cparse.nim(1635)         expression
cparse.nim(1413)         startExpression
cparse.nim(295)          eat
cparse.nim(136)          parMessage
clex.nim(139)            lexMessage
home/sakae/c2nim/doc/gmp.h(362, 33) Error: ')' expected

debugオプションを付けると、c2nimの考え方が少しは理解出来るかなあ? おいらは理解出来ないので、 見なかった事にして(コメント化)先へ進みます。で、エラー次ぐエラーで断念しました。

で、gauche.hを変換出来るかと思ったら、他のヘッダーファイルを沢山呼び込んでおり、c2nimの制限から 断念しました。まだまだ、簡単にはいかないですな。