retro on F#

『おみやげと鉄道』(講談社)なんて本を読んだ。おみやげって英語では、スーベニアって 言うんだけど、日本で言うおみやげとスーベニアには、性格に違いが有ると言う。

欧州圏でのスーベニアは、旅のメモリアルを主目的にしてるらしい。それに対して土産は、 旅に行ってきましたよ。ほれ、これがその証拠とばかり近隣に配るのが連綿と続いている ようだ。

会社時代、非常に出張が多かった。職場への土産は、まあ潤滑油だな。誰彼となく出張に 行くもので、あちこちの土産が溢れていたよ。時々、外国から来る同僚なんかが居て、 結構外国の土産も堪能出来たなあ。まあ、普通に言ったら、土産ってのは食べ物に決まって いる。食べたら無くなるから、良いのだ。

自分用の土産は、残る物が有ったな。かさばる物は駄目って事で。スイスの超美しい地図。 本場ギネスのコースター。ゾーリンゲンの鋏とか。食べ物って言うと、アラスカの空港で 売ってた、スモークサーモン。これとセットで、バーボンウィスキーとか。これを買うのが 楽しみでしたよ。

件の本に色々な土産が載ってた。昔住んだ事がある仙台。仙台と言えば『萩の月』が有名。 これが全国に知れ渡るようになったのは、その頃仙台、福岡間に路線を持っていた東亜国内航空の 機内スナックに採用されたかららしい。由緒ある物だと思っていたけど、そうじゃ無かったのね。 初めて知りましたよ。

初めて知ると言うと、東京名物とばかり思っていた、ひよこのお菓子と言うか饅頭もどきのやつ。 出身は、九州は飯塚だそうだ。全国区に販路を拡げたんだな。萩の月は、これとは対象に 地方区より外には出ていない。食べたかったら仙台方面まで足を延ばしてくださいって事だ。

大阪にアリバイ横丁ってのがあるらしい。そこでは全国各地の土産を売っていて、ほら仙台に 行ってきたんだよと証拠品を買い求める事が出来るらしい。尤も、そういうものに頼らなくても 、アンテナショップに行けば置いてあるのかな?

retro.fsx

再びしつこくretroします。vmの所を見ていると、retro.fsxなんてファイルが置いてある。 fsxなんて特撮ですか? ファイルを開いてみるとどうもmlっぽい。何故mlと推測したかと 言うと、特徴的なletとmatchってのが有ったからです。こういうサフィックスを 付けるのはM$かな。とするとF#語で書かれてるんだな。

えっと、F#を動かすには、M$お得意の毎年更新されるvisual stdioを入れて、それから M$研究所へ行って、F#を取ってくるのかな。でも、家の掟として、visual stdioとeclispeと 言うGUI系は入れてはいかんとなってるしなぁ。諦めろ。諦められない!

最後の望みをFreeBSDに託して、portsの下を漁ってみると、lang/fsharpなんてのが出て きたぞ。俄かに信じられんな。広告を読んでみた。

F# is an open-source, strongly typed, multi-paradigm programming
language encompassing functional, imperative and object-oriented
programming techniques.  F# is most often used as a cross-platform CLI
language, but can also be used to generate JavaScript and GPU code.

F# is developed by The F# Software Foundation and Microsoft.  An open
source, cross-platform edition of F# is available from the F# Software
Foundation.  F# is also a fully supported language in Visual Studio.
Other tools supporting F# development include Mono, MonoDevelop,
SharpDevelop and the WebSharper tools for JavaScript and HTML5 web
programming.

F# originated as a variant of ML and has been influenced by OCaml, C#,
Python, Haskell, Scala and Erlang.

WWW: http://fsharp.org/

で、http://fsharp.org/へ行ってみましたよ。そしたら、確かに FreeBSDでのF#の作り方が出てた。それによると、monoってのが最初に必要らしい。monoって 一体何? やはり広告を読んでみた。

Mono is an open source implementation of .NET Development Framework. Its
objective is to enable UNIX developers to build and deploy cross-platform
.NET Applications. The project implements various technologies developed by
Microsoft that have now been submitted to the ECMA for standardization.

Mono provides the necessary software to develop and run .NET client and
server applications on BSD, Linux, Solaris, Mac OS X, Windows, and Unix.

WWW: http://www.mono-project.com/

ドット・ネットの土台なのね。待つ事数十分。出来上がりましたよ。fsharpiってのが F#のインタープリタで、fsharpcってのがコンパイラか。早速コンパイラを試してみる。

[sakae@pcbsd ~/src/ml]$ fsharpc retro.fsx
F# Compiler for F# 3.0 (Open Source Edition)
Freely distributed under the Apache 2.0 Open Source License

/usr/home/sakae/src/ml/retro.fsx(114,13): warning FS0058: Possible incorrect indentation: this token is offside of context started at position (113:22). Try indenting this token further or using standard formatting conventions.

/usr/home/sakae/src/ml/retro.fsx(114,13): warning FS0058: Possible incorrect indentation: this token is offside of context started at position (113:22). Try indenting this token further or using standard formatting conventions.

ml系にはオフサイドが無いと思っていたけど、ここら変はhaskellの真似をしてるのね。 2回も同じ警告が出てくるけど、ちょっとしつこくないかい。あそうか、2PASS方式で コンパイルしてるんか。 で、 出来上がったのは、いやーんになっちゃうexeですよ。

[sakae@pcbsd ~/src/ml]$ file retro.exe
retro.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows

Windowsへ持って行って実行しろとな。それともワインを入れろ? ワイン用の葡萄なら そこら辺にたくさんあるんだけど、今回は遠慮して、Windows7でやってみます。

sakae@NIL ~/retro
$ ./retro.exe

ハンドルされていない例外: System.IO.FileNotFoundException: ファイルまたはアセン
ブリ 'FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d5
0a3a'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つ
かりません。
   場所 <StartupCode$retro>.$Retro$fsx.main@()
Signal 67

ははは、見事にエラー。でも相変わらずアホなメッセージを出すな。指定されたファイルって、 何やねん。一番大事な事が抜けてるな。これがM$の流儀だからしょうがないか。

文句を言っててもしょうが無いので、前向きに対処しよう。マニュアルは無いようなので 本体に同梱されてないか試してみた。

[sakae@pcbsd ~/src/ml]$ fsharpc --help
   :
--standalone                   Statically link the F# library and all
                               referenced DLLs that depend on it into the
                               assembly being generated
    :

全部入りってモードが指定出来るのね。これなら、何か足りないとは言われはしないだろう。 その代わり出来上がったものは肥大化するけどね。

sakae@NIL ~/retro/benchmarks
$ time ../main.exe

real    0m4.239s
user    0m0.000s
sys     0m0.031s

sakae@NIL ~/retro/benchmarks
$ time ../sretro.exe

real    0m7.925s
user    0m0.015s
sys     0m0.031s

golangとのスピード比べには負けてしまいましたよ。

mono

fsharpを入れる時の説明を読んでたら、/usr/local/etc/monoの下のレイジストリィを いじれなんて事が書いてあった。そんなファイル無いんだけどな。

[sakae@pcbsd /usr/local/etc/mono]$ ls -F
2.0/            4.5/            config
4.0/            browscap.ini    mconfig/

4.0とかあるのは、Windowsにいろいろ入ってるNETフレームワークの事だな。でも何となく configってファイルが気になるんで、ちょっと見てみる。

<configuration>
        <dllmap dll="i:cygwin1.dll" target="libc.so.7" os="!windows" />
        <dllmap dll="libc" target="libc.so.7" os="!windows"/>
        <dllmap dll="intl" target="libintl.so" os="!windows"/>
             :

この設定ファイルって、Windows側とFreeBSD側のライブラリィーの対応表じゃなかろうか。 osがwindowsじゃ無い場合って、cygwinの事なんだな。わざわざ、こんな物を用意してるって 事は、ひょっとして、fsharpcでコンパイルした、exeファイルも、そのままFreeBSDで 動くに違いない。ぐぐる様に聞いてみましたよ。

.NETアプリをLinux対応にするMono なんてのを教えてもらった。monoってコマンドが、Windows上のネット・フレームワークの まねをしてくれるのね。

[sakae@pcbsd ~/src/retro/benchmarks]$ time ../main

real    0m4.340s
user    0m4.139s
sys     0m0.063s
[sakae@pcbsd ~/src/retro/benchmarks]$ time mono ../retro.exe

real    0m13.126s
user    0m11.762s
sys     0m1.254s
[sakae@pcbsd ~/src/retro/benchmarks]$ time fsharpi ../retro.fsx

/usr/home/sakae/src/retro/retro.fsx(114,13): warning FS0058: Possible incorrect indentation: this token is offside of context started at position (113:22). Try indenting this token further or using standard formatting conventions.

real    0m46.291s
user    0m25.528s
sys     0m20.048s

ふむ、FreeBSD上で(無理して)exeファイルを動かすと、Windows上で素直に動かすよりも 2倍時間がかかっているな。インタプリタで動かすと、rubyやPythonで動かすよりも断然 速い。インタプリタは、ソースファイルを1回しかスキャンしないのね。なんて事が 分かったよ。

ついでに、monoって何よってのをmanを引いておく。

DESCRIPTION
       mono is a runtime implementation of the  ECMA  Common  Language  Infra-
       structure.  This can be used to run ECMA and .NET applications.

       The runtime contains a native code generator that transforms the Common
       Intermediate Language into native code.

       The code generator can operate in two modes: just in  time  compilation
       (JIT)  or  ahead  of time compilation (AOT).  Since code can be dynami-
       cally loaded, the runtime environment and the JIT are  always  present,
       even if code is compiled ahead of time.

       The  runtime  loads  the specified file and optionally passes the argu-
       ments to it.  The file is an ECMA assembly.  They typically have a .exe
       or .dll extension.

       The  runtime  provides  a  number  of configuration options for running
       applications, for developing and debugging, and for testing and  debug-
       ging the runtime itself.

       The  mono  command  uses the Boehm conservative garbage collector while
       the mono-sgen command uses a moving and generational garbage collector.

ATOとかJITが入ってますよ。ガベコレも入ってますよって事ですな。ATOとJITを試して みたけど、デフォルトとの差は誤差範囲でしたよ。今回みたいなベンチだと大いに ターボチャージャーみたいに効きそうな感じがするんですけど、そんなに甘い世界じゃ無い? 詳しい事は、ruby方面のあの人に聞いてみればいいのか。

あの人に聞くには、資料に何を用意すればいいの。取り合えず逆アセンブラの結果でも 見せればいいのかな?

[sakae@pcbsd ~/src/ml]$ monodis retro.exe
.assembly extern mscorlib
{
  .ver 4:0:0:0
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
}
.assembly extern FSharp.Core
{
  .ver 4:3:0:0
  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
}
.assembly 'retro'
{
  .custom instance void class [FSharp.Core]Microsoft.FSharp.Core.FSharpInterface
DataVersionAttribute::'.ctor'(int32, int32, int32) =  (
                01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 ) // ................
       :
    // method line 37
    .method assembly static
           default int32 readFile@74 (int32 h)  cil managed
    {
        // Method begins at RVA 0x2594
        // Code size 25 (0x19)
        .maxstack 4
        .locals init (
                int32   V_0)
        IL_0000:  call class [mscorlib]System.IO.FileStream[] class Retro::get_files()
        IL_0005:  ldarg.0
        IL_0006:  ldelem [mscorlib]System.IO.FileStream
        IL_000b:  callvirt instance int32 class [mscorlib]System.IO.Stream::ReadByte()
        IL_0010:  stloc.0
        IL_0011:  ldloc.0
        IL_0012:  ldc.i4.m1
        IL_0013:  bne.un.s IL_0017

        IL_0015:  ldc.i4.0
        IL_0016:  ret
        IL_0017:  ldloc.0
        IL_0018:  ret
    } // end of method Retro::readFile@74
           :

これも付けた方が良いのかな?

[sakae@pcbsd ~/src/ml]$ pedump retro.exe
COFF Header:
                        Machine: 0x014c
                       Sections: 0x0003
                     Time stamp: 0x52185ec9
        Pointer to Symbol Table: 0x00000000
                   Symbol Count: 0x00000000
           Optional Header Size: 0x00e0
                Characteristics: 0x010e

PE Header:
                 Magic (0x010b): 0x010b
                     LMajor (6): 0x08
                     LMinor (0): 0x00
                      Code Size: 0x00003c00
          Initialized Data Size: 0x00000200
        Uninitialized Data Size: 0x00000000
                Entry Point RVA: 0x00005abe
                  Code Base RVA: 0x00002000
                  Data Base RVA: 0x00006000
     :
Data directories:
             Export Table: 0x00000000 [0x00000000]
             Import Table: 0x00005a64 [0x00000057]
           Resource Table: 0x00000000 [0x00000000]
          Exception Table: 0x00000000 [0x00000000]
        Certificate Table: 0x00000000 [0x00000000]
              Reloc Table: 0x00008000 [0x0000000c]
                    Debug: 0x00000000 [0x00000000]
                Copyright: 0x00000000 [0x00000000]
               Global Ptr: 0x00000000 [0x00000000]
                TLS Table: 0x00000000 [0x00000000]
        Load Config Table: 0x00000000 [0x00000000]
             Bound Import: 0x00000000 [0x00000000]
                      IAT: 0x00002000 [0x00000008]
        Delay Import Desc: 0x00000000 [0x00000000]
               CLI Header: 0x00002008 [0x00000048]

        Name: .text
           Virtual Size: 0x00003ac4
        Virtual Address: 0x00002000
          Raw Data Size: 0x00003c00
           Raw Data Ptr: 0x00000200
              Reloc Ptr: 0x00000000
             LineNo Ptr: 0x00000000
            Reloc Count: 0x0000
             Line Count: 0x0000
        Flags: code, exec, read,
       :

mcs, csharp

monoを入れると、M$がやっきになって宣伝してるC#のコンパイラとインタプリタが同時に インストールされる。以下は、cshapeのman部分。

DESCRIPTION
       The csharp is an interactive C# shell that allows the user to enter and
       evaluate C# statements and expressions from  the  command  line.    The
       regular  mcs  command  line  options can be used in this version of the
       compiler.

       The gsharp command is a GUI version of the  C#  interpreter  that  uses
       Gtk#  and provides an area to attach widgets as well.      This version
       can be attached to other Gtk# applications in a safe way as it  injects
       itself  into the main loop of a Gtk# application, avoiding any problems
       arising from the multi-threaded nature of injecting itself into a  tar-
       get process.

       Files  specified  in  the  command  line will be loaded and executed as
       scripts.

Javaもどき言語を手打ちで試す人って居るのかしらん? まあ、正統Javaに勝ちたい一心で ってのが、あるんでしょうなあ。

また、monoをGUIで使いたいって人用にmonodevelopなんてのが用意されてて、Windowsから 移ってきたクリック猿な人も安心。更に、mono-basicなんてのも有ったりして、幅広く Windows屋さんを呼び込むのみ貢献してる。日経ソフトウェアだか、日経Linuxあたりで 紹介されんかな。XPが時間切れで、Linuxへどうぞって事なら、外しちゃだめよ。

おまけ

retroのvmコレクションの中に、C#のコードが有ったので、試してみた。

[sakae@pcbsd ~/src/retro/vm/experimental/CSharp]$ mcs RetroForth.cs
[sakae@pcbsd ~/src/retro/vm/experimental/CSharp]$ mv RetroForth.exe ~/src/retro/
[sakae@pcbsd ~/src/retro/vm/experimental/CSharp]$ cd ~/src/retro/benchmarks/
[sakae@pcbsd ~/src/retro/benchmarks]$ time mono ../RetroForth.exe

real    0m4.971s
user    0m4.744s
sys     0m0.133s

ふーん。golangの後を追うって感じだな。猫でも分かるC# なんて本も出てるようだから、 遊んでみるには良いかも知れませんよ。おいらは手を出さないけど。

あっ、実験用コードって事で、ネットワーク上にパケットが流通するようにvmが拡張 されてる。そして、それを使った、TCPのサーバーとクライアントの例が出てた。 VM拡張の良い例になってるな。