日本語プログラミング言語をトランスパイルする際迷うことを考えてみる

# はじめに

@mylifewithviolin さんに「[日本語構造化仕様記述言語 Re:Mind（リマインド） Advent Calendar 2023](https://qiita.com/advent-calendar/2023/remind)」に招待いただいたので、自分が日本語プログラミング言語（トランスパイラ）を作成する際に迷う or 検討が必要そうな項目を書き出してみました。

:::note info
（おことわり）
私はトランスパイラも日本語プログラミング言語処理系も書いたことはありません（作成したのは英語ベースのインタープリタ言語のみ）。机上の空論もあると思いますのであらかじめご了承ください。
また、Re:Mindで方針が決まっているかどうかにかかわらず、あくまで「日本語プログラミング言語を新規に作る場合どんなことを考えるか」の観点で考えました。
:::

# 文法のトレードオフ
トランスパイラなので、ターゲット言語のソースコードを出力します。
その際、文法の自由度、ターゲット言語の種類と実装コストはトレードオフになります。

## 予約語置換ですます
- 実装コスト: 低
- 文法の自由度：低
- ターゲット言語の種類：低

一番シンプルな仕組みとして、予約語のみ日本語で再定義し、トランスパイラは機械的に単語を置き換えます。

```java:イメージ
クラス 有料会員 継承 会員 {}
// ↓
class 有料会員 extends 会員 {} 
```

例として、Hedyではこの方法で30以上の言語に対応しています。

https://github.com/hedyorg/hedy/blob/main/grammars/keywords-ja.lark

```lark:予約語の対応表
_PRINT: ("かけ" | "print") _SPACE?
_ASK: ("きけ" | "ask") _SPACE?
_ECHO: ("まね" | "echo") _SPACE?
_FORWARD: ("すすめ" | "forward") _SPACE?
_TURN: ("まわれ" | "turn") _SPACE?
left: ("ひだり" | "left") _SPACE?
right: ("みぎ" | "right") _SPACE?
black: ("くろ" | "black") _SPACE?
...
```

実装が簡単ですが、トランスパイラの場合はターゲット言語が1つに限定されてしまう問題があります。

## 構文を自前で用意する
- 実装コスト: 中
- 文法の自由度：高
- ターゲット言語の種類：中

構文自体を独自定義すれば、文法の異なる複数の言語をターゲットとして出力可能です。
一方、型システムの差異の問題は依然として残ります。
例えば、`String` 型のデフォルト値が `""` になる言語と `null` になる言語、トランスパイル先によって挙動が異なってしまうとバグのもとになります。

## 型システムも自前で用意する
- 実装コスト: 高
- 文法の自由度：高
- ターゲット言語の種類：高

ここまでくれば上記の問題も解決しますが、もはや0から新しいプログラミング言語を作っているのと同じ状況です。
いっそ、「JVM or .NETベースで他言語資産が使える新言語」としてしまっても良いかもしれません。

# 予約語衝突問題
日本語プログラミング言語なので、予約語も日本語です。使いたい変数名が予約語と衝突してしまう可能性もあります。
ユビキタス言語を翻訳不要で使えるのが日本語プログラミング言語の強みなので、考慮しておくとユーザー体験が良くなりそうです。

## 案1: 命名規則を変える
1つ目の対策案として、予約語と変数名の命名規則が被らないようにします。

例として、Zimbuでは変数は小文字始まり(`hoge`)、予約語は全て大文字(`FUNC`)という規則を持っています。

https://moolenaar.net/z/zimbu/spec/zimbu.html

日本語だと普段予約語に使わなそうなカタカナで表すのが良いでしょうか？（例：`クリカエシ`, `カンスウ`）見慣れるまでは違和感があるかもしれません。

## 案2: 表記を変える
2つ目の案は変数表記を予約語と変える方式です。Perlでは、変数の先頭に `$` を付ける必要があります[^sigil]。

## 案3: 予約語を使わない
3つ目の案として、そもそも予約語を使わず特殊記号のみで構文を表します。func, class, if, forは記号に置き換えても違和感がありませんが、 return (特に早期return)はどんな記号だとしっくりくるかあまりイメージが付きません...

# 利用ライブラリから漏れ出す英語
日本語化するうえで一番影響が大きい可能性があるのがライブラリです。
ターゲット言語の標準パッケージ、メソッド、クラスはそのままでは英語なので、自前で定義した関数、クラス以外は英語の知識が必要となってしまいます。

## 案1: パッケージごとに逐語訳（？）する
1つ目の解決案として、パッケージ名やメソッド名に逐一日本語名を対応させます。
パッケージ提供元が要しなくても、ターゲット言語のパッケージに対応する逐語訳定義のみ提供できるようにする（イメージはTypeScriptの型定義ファイル）ことでサードパーティが提供することも可能でしょう。

とはいえ、実装コストが高くバージョンアップに追随する保守コストも高くなります。また、定訳が無いと学習コストも上がってしまいます。訳に迷うメソッド名もあるでしょう（例：`extract` は `抽出`？ `展開`？）。

## 案2: ドメインモデルのみ日本語プログラミング言語を使用する
2つ目は、そもそも使う領域をライブラリが登場しない範囲に絞るという案です。

日本語プログラミング言語を使う動機の1つに「自然言語で交わされる仕様と、実際の設計、実装の乖離を減らしたい」というものがあると考えています。これはドメイン駆動設計のユビキタス言語の考え方に通じるものがあり、日本語プログラミング言語をドメインモデル（およびドメインモデルによって記述されるアプリケーション層）に適用するだけでもある程度の効果を発揮するのではないかと思いました。

ドメインモデルだけであれば、自前クラス、インターフェース（＝名前が日本語）のみで完結できます[^model]。
一方、特定技術のパッケージ（＝名前が英語）に依存する実装（ドメインモデルの具象クラス等）は、従来通りターゲット言語でそのまま英語でプログラミングします。

とはいえ、ドメインモデルでも言語依存の要素は消せない（例：ターゲット言語がJavaであれば、ドメインエラーは `Exception` を継承するしリポジトリの戻り値に `Optional` を使うこともある）ため、ある程度は融通を効かせて使用していく必要はありそうです。

# おわりに
ここまでお読みいただきありがとうございました。ユースケースがある程度明確になっている分、考えることも多かった印象があります（実際の実装がないのでふわふわした話になってしまいましたが…）。

[^sigil]: `$` 以外にも `@`, `%`, `\` 等も使用し、データ構造を明示する役割もあります。
[^model]: ドメインモデル定義専用でライブラリを触らないのであれば、ターゲット言語複数対応のハードルも下がるでしょう。