Skip to content

Commit

Permalink
added module2
Browse files Browse the repository at this point in the history
  • Loading branch information
hsk committed Feb 14, 2015
1 parent 388ad3c commit 6bd7978
Show file tree
Hide file tree
Showing 22 changed files with 2,840 additions and 15 deletions.
8 changes: 5 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
現在、AltJSと呼ばれる言語群が存在します。これはJavaScriptへのトランスレータであるプログラミング言語の総称です。
λ計算のAltJSも数多く存在しています。
しかしながら、λ計算ベースの言語からJavaScriptへの変換後のコードは余美しい物ではありません。
なぜならば、λ計算の変換過程での正規形にはA正規形や、k正規形、β正規形等があるが、広く知られているJavaScriptへの変換用の正規形が知られていない為です。
そこで、この文章では、JavaScriptへの単純な変換方法について述べ、より発展的なJavaScriptへの変換を提案します。
様々なプログラムが正確に動作することが重要なので、可読性は犠牲になっているのでしょう。
λ計算の変換過程での正規形にはA正規形や、k正規形、β正規形等があります。
この文章では、JavaScriptへの単純な変換方法について述べ、より発展的なJavaScriptへの変換を提案します。

## 2. イントロダクション

現在、AltJSと呼ばれる言語群が存在します。これはJavaScriptへのトランスレータであるプログラミング言語の総称です。
js\_of\_ocaml、Elm等、λ計算のAltJSも数多く存在しています。
しかしながら、λ計算ベースの言語からJavaScriptへの変換後のコードは美しい物ではないようです。
なぜならば、λ計算の変換過程での正規形にはA正規形や、k正規形、β正規形等があるが、広く知られているJavaScriptへの変換用の正規形が知られていない為のように思います。
なぜならば、λ計算の変換過程での正規形にはA正規形や、k正規形、β正規形等があります。
同じようにJavaScriptへの着実で奇麗な変換方法があれば良いでしょう。

## 2.1. 各正規形について

Expand Down
10 changes: 7 additions & 3 deletions docs/module/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
default: module13 run
default: module15 run

run:
./module13
./module15

module02: module02.ml
ocamlfind ocamlc -package ppx_deriving.show module02.ml -o module02
Expand Down Expand Up @@ -55,5 +55,9 @@ module14: module14.ml
ocamlfind ocamlc -package ppx_deriving.show module14.ml -o module14
./module14

module15: module15.ml
ocamlfind ocamlc -package ppx_deriving.show module15.ml -o module15
./module15

clean:
rm -rf *.cm* module02 module03 module04 module05 module06 module07 module08 module09 module10 module11 module12 module13 module14
rm -rf *.cm* module02 module03 module04 module05 module06 module07 module08 module09 module10 module11 module12 module13 module14 module15
244 changes: 235 additions & 9 deletions docs/module/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

## 1. はじめに

モジュールの機能があれば、ファイルを複数に分けてプログラムを作成出来て便利です。しかし、作るノウハウがまとまっている文章はあまり見た事がありません。そこで、ここでは、OCamlのモジュールシステムをシミュレーションしてみます。シミュレーションするにあたって、データ構造を作成し、ファイルシステムからの読み込みを行えるようにします。出来たら、数字を計算するだけのインタプリタを作成してうまく動いたら、拡張して行きます。そこそこ動くようになったら、ネイティブな関数も呼べるようにして印字機能を作ります。
とりあえず、15.6からで良いと思われます。

モジュールの機能があれば、ファイルを複数に分けてプログラムを作成出来て便利です。ここでは、OCamlのモジュールシステムをシミュレーションしてみます。シミュレーションするにあたって、データ構造を作成し、ファイルシステムからの読み込みを行えるようにします。出来たら、数字を計算するだけのインタプリタを作成してうまく動いたら、拡張して行きます。そこそこ動くようになったら、ネイティブな関数も呼べるようにして印字機能を作ります。

## 2. データ構造

Expand Down Expand Up @@ -687,9 +689,229 @@ read c.ml

ファイルを何度もOpenするのでは効率がよくありません。ファイルをキャッシュしましょう。

今まで環境は、変数名と値のリストでした。ここに、ファイル名と環境を追加します。
ファイルを読み込み、コンパイルした結果も保存するのです。

あれ、でも保存してた気がする?いや、今保存しているのはモジュールそのものです。
`A.print_int_ln`を見つけると、モジュールを読み込みます。これは良いのです。
Openした場合に、モジュールが保存されません。これが問題です。Openした場合にファイルを開いたのであれば、その結果をモジュールとして保存しましょう。否、モジュールを読み込んだ後に、Openで展開するように処理を変えましょう。

うーん。結局うまく行かない理由は、Letの処理にありました。Letでは、環境を捨てます。名前は捨てないと行けないのです。でも、読み込んだモジュールは保持したい。従って、Letの後に環境からモジュールを検索してモジュールのみ取り出します。これで良いはずです。

しかし複雑になってしまいました。

## 15. 依存フローを作成する

## 16. 依存解析する

## 15.1. 作って見る

ocamlのプログラムの場合は、実はOpenがあったらとか、モジュールが使われていたら、そのとき読み込まれるというわけではありません。予め読み込み順を指定しておき、その順番に読み込まれます。

しかしながら、それでは、面倒です。自動で依存関係を調べて勝手に読み込んでくれたら良いのにと思います。

そこで、自動的に解析される事を考えます。

コンパイラを作成する場合は、型推論を行う場合や、変数の型を知りたい場合等、依存元を先にコンパイルする必要があるかもしれません。


処理方法は、メインファイルが依存しているファイルは全てオープンし、そのより上のファイルを読み込んで行く事で、全てのファイルを認識出来ます。
どのファイルがどのファイルを読み込んでいるかを記録しておくと、それはグラフの情報となります。
依存解析をすることで、コンパイル順を決定する事が出来るでしょう。
ここでは、変数の参照と、Openのみを解析して、読み込みフローを作成します。


嫌な例を考えよう。

```
g.ml
module B = struct
module C = struct
let c = 1
end
end
h.ml
open G
open B
open C;;
print_int c
```

これが正しく動く必要があります。これを正しく動作させることを考えなくては行けません。
このような例が沢山必要です。やっと問題の本質に行き着いたのかな。
モジュールシステムの最も重要な本質的な部分を考えましょう。
例えば、ファイル名の一文字目を大文字に変えるとかは本質的な処理ではありません。
2項演算子も、依存の解析には重要でありません。重要なのは、openとmoduleと変数です。OpenとModとVarで抽象的なデータ構造を作る事こそが本質でしょう。

まず、変数は捨てましょう。最も重要なのは、OpenとModとファイルです。

```
type e =
Unit
Open of string * e
Mod of string * e * e
type f =
FUnit
FFile of string * e * f
let files =
FFile("G", Mod("B",Mod("C",Unit,Unit),Unit),
FFile("H", Open("G",Open("B",Open("C",Unit))),
FUnit))
```

この小さいデータ構造で、出来る可能性について検討しましょう。
どうやって解析すべきなんでしょうね。よくわからないので、評価してみましょう。

```
let rec eval = function
| Unit -> []
| Open(x,e) -> x::(eval e)
| Mod(x,e,e2) -> x::(eval e)@(eval e2)
let rec evalf = function
| FUnit -> []
| FFile(x,e,f) -> x::(eval e) @ (evalf f)
let _ =
Printf.printf "[%s]\n" (String.concat ";" (evalf files))
```

とりあえず、出てくる名前を単純に書き出してみました。

```
[G;B;C;H;G;B;C]
```

いいですね。でもこれは、全部リストにしてしまってるのでちょっと違いますね。

ファイル毎に分けてみましょう。


```
let rec eval = function
| Unit -> []
| Open(x,e) -> x::(eval e)
| Mod(x,e,e2) -> x::(eval e)@(eval e2)
let rec evalf = function
| FUnit -> []
| FFile(x,e,f) -> (x,(eval e)) :: (evalf f)
let _ =
Printf.printf "%s\n" (show_sss (evalf files))
```

```
[("G", ["B"; "C"]); ("H", ["G"; "B"; "C"])]
```


Gではモジュール作っただけで、依存してませんよ。ってことで、じゃあ削りましょう。

```
let rec eval = function
| Unit -> []
| Open(x,e) -> x::(eval e)
| Mod(x,e,e2) -> (eval e)@(eval e2)
let rec evalf = function
| FUnit -> []
| FFile(x,e,f) -> (x,(eval e)) :: (evalf f)
let _ =
Printf.printf "%s\n" (show_sss (evalf files))
```

```
[("G", []); ("H", ["G"; "B"; "C"])]
```

でも、HはBやCは依存しませんよ。間違えてます。それに、メインのモジュールはHです。

```
let rec read (file:string) = function
| FUnit -> assert false
| FFile((x:string),(e:e),_)
when x = file ->
e
| FFile(_,_,xs) -> read file xs
let rec eval files = function
| Unit -> []
| Open(x,e) -> x::(eval files e)
| Mod(x,e,e2) -> (eval files e)@(eval files e2)
let rec evalf files file =
let e = read file files in
eval files e
let _ =
Printf.printf "%s\n" (show_ss (evalf files "H"))
```

```
["G"; "B"; "C"]
```

HはGに依存してます。それは結構ですけど、BやCはGの物です。これを解決しないといけませんよね。
うーん、、、。ハマった。これって、今までやって来た事を繰り返してません?

## 15.2. いったんまとめる

一度奇麗にしましょう。

## 15.3. 削る

1,2節では、ハマってしまいました。14章まで積み上げて来た物をぶっ壊して1から勿体ないですね。
考え方を変えるんだ!14章のプログラムから削りだせば良いんだ。

## 15.4. リファクタリングする

15.3節のプログラムをリファクタリングして15.2の形に合わせましょう。

## 15.5. 読み込み順のソート

読み込み順のソートを行えば、問題ないはずです。
相互参照もチェックできるようにしました。


## 15.6. ファイル読み込みに環境を渡さない。

ファイル読み込みするときに、必要以上に検索しないようにする為のキャッシュが効いています。
でもバグってしまいます。困った。ここで、一度副作用有りで作ってみましょう。
環境を受け渡しすれば速いけど、分かり辛くなるのは問題です。

再考しましょう。

まず、内部モジュールが邪魔です。これは明らかです。

Openがあるだけの物で、しっかり作り直します。
これで、しっかりとした解析をする事が基本になります。

## 15.7. 環境を拡張しやすいようにする

環境がただのリストでしたが、複数追加したくなる可能性を感じたので、書き換えます。

## 15.8. 内部モジュールで拡張しなおす

拡張しました。

## 15.9. 変数に対応する。

変数はオープンした場合とは違って、一時的な参照に過ぎません。
開かれた履歴は残しますが、それだけです。

## 16. 変数について解析する

15章ではOpenとModuleについての解析が出来ました。
このアルゴリズムは、ファイルを1度ずつ読み込み、読み込み順を一気に求めます。
これは一見問題ないように思うかもしれませんが、相互依存しているファイルを見つける事は出来ません。

## 18. グラフの作成と依存解析

15章では、コンパイラ内部で行うには十分な読み込み順の検索が出来ました。

## 17. まとめ

Expand All @@ -698,13 +920,17 @@ read c.ml
評価器に十分な機能を追加した後、Openしたり、モジュールの読み込みを作成してみました。

今回はインタプリタの作成ですが、このような考えを元にコンパイラへ応用を考えると良いでしょう。
実のところ、ファイルの依存関係はとくに、わざわざグラフを作らなくても済む事が分かったように思います。
コンパイラを作成する場合は、依存元を先にコンパイルすると、
型推論が着実に行えます。とりあえず、メインファイルが依存しているファイルは全てオープンし、そのより上のファイルを読み込んで行く事で、全てのファイルを認識出来ます。
どのファイルがどのファイルを読み込んでいるかを記録しておくと、それはグラフの情報となります。
依存解析をすることで、コンパイル順を決定する事が出来るでしょう。

$a \ne 0$, there are two solutions to \(ax^2 + bx + c = 0\) and they are
$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$

## リンク

smlで作ったモジュールシステム:

https://github.com/jordanlewis/simple-module-system

simple module system:


ocaml関連の論文

https://ocaml.org/docs/papers.html
Loading

0 comments on commit 6bd7978

Please sign in to comment.