読者です 読者をやめる 読者になる 読者になる

きっといろふさんはSML# が書ける

いろふ Advent Calendarも5日目に突入です。

前日は@さんのirof文が実装できたよ。そう、Scalaならね!でした。

いやー皆さん、面白い記事を書かれますねぇ。いろふさんを 実装したり創造したり世界にしたりにしたり…ちょっと私には思いつきませんでした。なので、ここは12ヶ月のうち9ヶ月ほど遭遇したよくわからない気合と妄想を駆使して物語を書きたいと思います。

あらかじめ断っておきますがほぼフィクションです。あと言い遅れましたが、タイトルは完全に釣りです。

それは寝ているときにやってくる

とある研究室の一角で、ぺんぎんは気を失ったように眠っていた。机の周りには書籍が散乱しており、目の前に置かれたPCは主の眠りを妨げまいと、静かにスリープモードへ移行している。
このままいけば、ぺんぎんは翌日に目を覚まして「嗚呼、またやってしまったか」などとぼやきつつ、日々の作業にいそしむことだっただろう。
しかしこの日は、少しだけ異なる事象が発生した。
『…ますか…聞こえますか…』
誰かが語りかけてきたのである。
『別世界線の○ろふです…あなたの夢の中で直接語りかけています…』
なぜ時事ネタなのか。なぜ伏字なのか。それ以前に、夢の中で語りかけるとはどういう状況だよと内心思いつつ、尋ねる気力もないぺんぎんは次の言葉を待つ。
『寝ている場合ではありません…SML#で…PFDSを写経するのです…』
どうやらどこかの世界の○ろふさんは、SML#がお好みのようだ。他人にお勧めしてくるあたり、既に自分は写経済みということなのだろう。
さて、このまま無視するという手もある。いくら○ろふさんの言葉とはいえ、こんな時間帯から起きて写経する意味はあるのだろうか。
そこまで考えてから、ふむ、とぺんぎんは肯定する。
おそらく『意味はある』。何よりノリと勢いでここまで生きてきたのだ、謎のパワー駆動というのも悪くない──

スタートはマニュアルから

SML#は日本語のドキュメントがあるので便利だ。

プログラミング言語SML#解説

ぺんぎんは基本的Windowsユーザだ。なので、MinGWのコマンドを叩いていく。"終わらないコンパイル"と呼ばれることもあるSML#のインストールだが、バイナリを利用したインストールはさほど時間のかかるものでもないようだ。
コンソール上をログが軽快に流れていくのを横目に、ぺんぎんは写経対象の本を本棚から取り出す。


"Purely Functional Data Structures"


そういえば、とぺんぎんは検索システムであるつぶやきを探す。発見。

少し話は異なるが、mzpさんもこんな文章を引用(?)している。ようするに、PFDSを読み写経することで後々役に立つということだろう。


そんなことを考えているうちにSML#のインストールが完了した。とりあえずバージョンを確認してみよう。

>smlsharp -v
SML# version 1.2.0 (2012-11-14 18:25:26 JST) for x86-mingw

無事最新版がインストールされたことに、ぺんぎんはひとまず安堵するのだった。

皆大好きStack

夜も更けてきたこともあり、ぺんぎんは今は最初に登場するStackのみ写経することにした。
まず、シグネチャを記述する。

(* stack-sig.sml *)
signature Stack =
sig
  type 'a Stack

  val empty : 'a Stack
  val isEmpty : 'a Stack -> bool

  val cons : 'a * 'a Stack -> 'a Stack
  val head : 'a Stack -> 'a (* raises EMPTY if stack is empty *)
  val tail : 'a Stack -> 'a Stack (* raises EMPTY if stack is empty *)
end

Stackという型、空のStackを生成するempty、Stackの中身が空かどうかを判定するisEmpty、要素とStackをもらって新たなStackを返すcons、Stackがemptyでない場合先頭要素を返すhead、Stackがemptyでない場合先頭以外のデータを含むStackを返すtailが存在することがわかる。
次に、組み込みデータ型のlistを使ったStackの実装を書いてみる。

(* list-stack.sml *)
structure List : Stack =
struct

  type 'a Stack = 'a list

  val empty = []
  fun isEmpty s = null s

  fun cons (x,xs) = x :: xs
  fun head s = hd s
  fun tail s = tl s
end

もう一つの実装は、custom data typeを使ったものだ。

(* custom-stack.sml *)
structure CustomStack : Stack =
struct
  datatype 'a Stack = NIL | CONS of 'a * 'a Stack

  val empty = NIL
  fun isEmpty NIL = true | isEmpty _ = false

  fun cons (x, s) = CONS(x, s)
  fun head NIL = raise Empty
    | head (CONS(x, s)) = x
  fun tail NIL = raise Empty
    | tail (CONS(x, s)) = s
end

ここまでは、SMLとなんら変わらない。まぁ、SML#はSMLの拡張言語なので当たり前の話なのかもしれない。
最後に、インタフェースファイルを記述する。

_require "basis.smi"
_require "stack-sig.sml"

structure Stack =
struct
  type 'a Stack (= boxed)
  val empty : 'a Stack
  val isEmpty : 'a Stack -> bool
  val cons : 'a * 'a Stack -> 'a Stack
  val head : 'a Stack -> 'a
  val tail : 'a Stack -> 'a Stack
end

ぺんぎんは、テストがないから自信ないな、とぼやくのであった。

夜明け

日が昇り始めたのを確認したぺんぎんは、急いで対話環境を立ち上げて簡単な動作確認をすることにした。

>smlsharp
#use "stack-sig.sml";
#use "list-stack.sml";
#List.head(List.cons(1,List.empty));
val it = 1 : int
#use "custom-stack.sml";
#CustomStack.head(CustomStack.cons(1,CustomStack.empty));
val it = 1 : int

とりあえずは動作しているようだ。ひとまず安堵。
次は分割コンパイルや残りの写経を頑張ろうと決意しつつ、日常に戻るぺんぎんであった──

まとめ

これは色々と黒歴史になりそうな記事ですねぇ…。

明日のアドベントカレンダーは@さんです。お楽しみに!