はじめに
FORTHで始まったスタック指向のプログラム言語は日本語とたいへん相性が良いということはもちろん当初から思っていたことですが、同時に、実は自然言語としての日本語がそもそもスタック指向なのではないかという気がしています。私は言語学者ではないのでそれ以上は何も言えないのですがそんなことをずっと考えています。
語順について
語順は大切なところなのであらためてまとめてみたいと思います。以下長くなり恐縮です。
語順についてまず浮かぶのは自然言語で英語の語順と日本語のそれが違うことです。次はよく引き合いに出されます。
(英語)
動詞+目的語
(日本語)
目的語+動詞
プログラム言語よりもう少し上の視点から見ても、今のコンピュータの操作が英語圏の人にとって都合のよいようにできています。シェルコマンドでも
echo "abc"
のように書きますが、先頭語にまずやりたい動作が書かれ、そのあとに付随した情報が書かれるので、見た目が動詞+目的語のスタイルで英語としてなじみが良いだけでなくこのコマンドを解析する側も容易です(最初のトークンを見ればおおまかな分岐ができるため)。
日本語表記のプログラム言語を作るにあたって真っ先に考えるのはやはりこの語順だろうと思います。
(英語風)
print("こんにちは");
に対しては、日本語だったらやはり、
(日本語風)
"こんにちは"を 表示
のような語順を採用したくなります。
これに対して二つアプローチがあります。
手法A:一般的な方法
一つは、一般的な方法で以下のようなものです。英語風と比較してみます。(日本語風のほうはMindにならって分かち書きとします)
(英語風)
全体を解析してからコード生成
printf("こんにちは")
(日本語風)
全体を解析してからコード生成
「こんにちは」を 表示する
上記のメリットは、解析後に一般的な方法でコード生成をすることができる点です。既存のプログラム言語のプリプロセッサとしても実現できるかもしれません。
手法B:根底からスタックの仕組みを取り入れる
もう一つは、(Mindがそうですが)根底からスタックの仕組みを取り入れる方法です。ソース解釈とコード生成は他のプログラム言語とだいぶ異なります。
(英語風)
全体を解析してからコード生成
printf("こんにちは")
(日本語風(スタック指向))
コード生成 コード生成
「こんにちは」を 表示する
上の日本語風のプログラムがコンパイル・実行されるのにこんな手順になります。
[コンパイル時]
(1)「こんにちは」を
→この語をコンパイルしコード生成する
(2)「表示する
→この語をコンパイルしコード生成する
[実行時]
(1)「こんにちは」をの対応コードを実行する
→この文字列がスタックに積まれる
(2)「表示するの対応コードを実行する
→表示するが呼び出される
→スタックから表示すべきデータを取り出して出力する
1語1語を単独でコンパイルし、その通りの順で1語1語を実行します。非常にシンプルな方法でもちろんコンパイラがやることもシンプルそのものです。
この方法のメリットは、本格的に日本語風表記に対応していくのにコンパイラが軽い、実装に無理が無いことです。
ここで、日本語風(スタック指向)(以下、日本語風と呼ぶことにします)のプログラムでの「語順」を考えてみます。
<文字列>を 表示する
という語順(大げさには文法)が規定されているように見えます。しかし先に書いたようにコンパイラは1語1語を別々にコンパイルしていくため、この2語にまたがって解釈することはありません(エラー検出などまたがって見ることもありますがここでは触れません)。つまり厳密には語順の規定はありません。
では上の「表示」について何も規定が無いのかと言えば有ります。それはスタック仕様です。
たとえばMindのランタイムライブラリ内での「表示」の定義で(これもMind記述)次のように書かれています。
表示とは (文字列 → ・)
・・・・・
・・・・・
上記で (文字列 → ・) はMindではコメント扱いですがコメントとして大事なことを書いています。この語がスタックから文字列を受け取り、スタックを空にしてで帰ることを表しています。(どんな書き方もでいいのですが私はこのように書いています)
日本語風のプログラムでは、ある処理が必要とするデータはソース上、その語の左側に記述されるものではなくて、その処理が呼び出された時にスタックに存在するものが処理されるデータとなります。
そうはいっても、実際のソース記述では処理の左側にデータを書くことが多いので、あたかも語順を規定しているように見える・・というものです。
語順規定ではなくてスタックの規定であることが分かるのが次の例です。
「こんにちは。」を 画面クリアしてから 表示する
上では「表示」のすぐ左に書かれているものは表示すべきデータではありません。データは「表示」から見ると一つ遠いところに書かれています。もし「画面クリア」がスタックの出し入れが無いニュートラルな語であればという前提ですが、これで正しく動作します。
面白いのは、上のプログラムが日本語としても不自然ではないことです。
こういう定義はどうでしょうか。
特別表示とは
画面クリアしてから 表示すること。
・・・とは
「こんにちは。」を 特別表示すること
上の「特別表示」の定義内で「表示」を使っていますが「表示」の直前どころか近傍のどこにも表示すべきデータが書いてありませんが、これで正しく動作します。そしてこれも、日本語としても不自然ではありません。
このような仕組みであっても結果的にうまく動作する上、日本語の言い回しともぴったり合致します。
2 + 3 の表記
話が遠回りになり恐縮です。2 + 3 の表記のお話に入ります。
先のような仕組みで動く日本語風プログラムでは数値演算(他の言語なら演算子のようなもの)もまた例外扱いせずに日本語風に書くのが望まれます。言語の一貫性ということもあります。たとえば、
2 + 3 を Aに 入れ
ではルールとしておかしなことになります。それで、
2に 3を 加え Aに 入れ
と書いて欲しい・・ということになります。これは 2 + 3 と 2に 3を 加え のどちらが優れているとか読みやすいか・・という問題ではなく、「こう書いてくださいね」という感じでしょうか。
Note
実はMindでは数式表現をサポートしています。
[2 + 3]を Aに 入れ
と書けます。しかしややこしいのであえて触れません。
数式表現(特に2項演算子を使った表記)は"たまたま"我々が小学校のころから習った表記であるため、「こっちじゃダメなの?」と思ってしまうのは自然なことだと思います。でもMindではなるべく特別扱いはしないようにしました。
一般論としてプログラム内に似たような記述があったら外に出して別定義とすることが推奨されますが、次のようなものはどうでしょうか。
・・・・A + 3 ・・・・
・・ B + 3 ・・・・・
・・・・ funcA(x) + 3 ・・・
上にプログラムには「+ 3」という表記がたくさんありますが、これだけ外に出そうとする人は居ないと思います。粒度が小さすぎて外に出すコストが高いという話は別としても、外に出した関数定義内で「+ 3」だけ書いても文法間違いになるから出せないというのが一番大きな問題です。+ という二項演算子がその両側に値を記述するのを要求するからです。
一方で、日本語風記述ではこんなプログラムが考えられます。
・・・ 3を 加え ・・・
・・ 3を 加え ・・・
・・・・ ○○したものに 3を 加え ・・・
上記の「3を 加え」を外に出すことができます。たとえばこんな感じです。
年齢補正とは
3を 加えること。
・・・とは
・・・ 年齢補正し ・・・
・・ 年齢補正し ・・・
・・・・ ○○したものを 年齢補正し ・・・
上記では冗長さを減らせただけでなく、「3を 加える」ことの意味が明白になっています。「年齢補正」の定義内では「加え」の左側に数値が一つしか書いてありませんが問題ありませんし、日本語として別に不自然ではありません。
このようなことができるのも、二項演算子さえもスタック指向にしたことの利点です。
構造体の定義について
次に、構造体の定義について私はこのように考えます。
構造体のような「データの配置についての書き方」は日本人の日常あまり書くことはないため、「いろいろな書き方がある」となるのは自然なことでそれはそれで良いと思います。
むしろ大事なのは、「日本人なら普通はこう書くよ」というような書き方があるものについてはなるべく尊重したいということです。そのようにすることでプログラム言語の習得を容易にできますし、習得した後であっても読みやすいことはプログラムの意図が通じやすいことにもなります。
たとえば、Mindに「引く」という単語があります。正規の書き方は
数値1から 数値2を 引く
ですが、この逆順の、
数値2を 数値1から 引く
という書き方もまた日本人は自然だと思うので、この両方を許容しています。ただ、この逆転介入はすべての語でやっているわけではないため完全ではありません。
ちなみにMindの構造体の定義はこんな感じになっています。(C, C++ でいうstruct は Mindでは「型紙」と呼んでいます)Mindのランタイムライブラリのソースの一部です。
日時型は 型紙
年は 変数
月は 変数
日は 変数
曜日は 変数
日時予備1は 変数
日付情報は 年と 月と 日と 曜日と 日時予備1
時は 変数
分は 変数
秒は 変数
時刻情報は 時と 分と 秒
全体は 日付情報と 時刻情報。
「ある集合にこんな要素が含まれる」というのは「○○は ・・と ・・と ・・」と列挙する表記が日本語として座りが良いかと思い上のようになっています。とはいえ、C の struct に似ていますね。
正直言いますと、構造体の定義のような割合高レベルなものは「加え」「表示」ほどには思い入れはなくて、わりあい簡単に決めています(^^;
おわりに
Mindユーザーの@mylifewithviolinさんのQiitaの記事についたコメントに対してコメントしていた内容ですが、@mylifewithviolinさんのおすすめもあって、記事にまとめておいてみました。