StarlarkとPythonの違い

# はじめに

StarlarkはPythonの方言で、Bazelの構成ファイル等で使用されています。

https://github.com/google/starlark-go

コマンドをインストールすれば、スクリプトの実行やREPLも使用可能です。

```
$ starlark
Welcome to Starlark (go.starlark.net)
>>> print("Hello, world!")
Hello, world!
```

基本的にPythonと互換性があるのですが、一部Starlark特有の機能もいくつかあるため記事にまとめました。

# 無限ループがデフォルトで無効

Starlarkはデフォルトでは無限ループできません。再帰や `while` を使うとエラーが発生します。

```
$ starlark
Welcome to Starlark (go.starlark.net)
>>> def fact(n):
...     if n <= 0:
...         return 1
...     return n * fact(n-1)
...
>>> fact(5)
Traceback (most recent call last):
  <stdin>:1:5: in <expr>
  <stdin>:4:17: in fact
  <stdin>:2:5: in fact
Error: function fact called recursively
```

明示的に `-recursion` フラグを指定すると使用可能になります。

```
$ starlark -recursion
Welcome to Starlark (go.starlark.net)
...
>>> fact(5)
120
```

無限ループを封じてチューリング完全でなくすることによって、bazel等の設定読み込み時にハングすることを防いでいると思われます。

> This rule, combined with the invariant that all loops are iterations over finite sequences, implies that Starlark programs can not be Turing complete unless the -recursion flag is specified.
>
> このルールが、すべてのループが有限のシーケンスであるという不変条件と組み合わさることによって、Starlarkのプログラムは `-recursion` フラグを指定しない限りチューリング完全になり得ません。

# if文とfor文をトップレベルで使用できない

```$ starlark
Welcome to Starlark (go.starlark.net)
>>> if True:
...     print("a")
...
<stdin>:1:1: if statement not within a function
```

```
$ starlark
Welcome to Starlark (go.starlark.net)
>>> for i in range(5):
...     print(i)
...
<stdin>:1:1: for loop not within a function
```

制御フローを分かりやすくするためと考えられます。

> Because if-statements and for-loops cannot appear outside of a function, control flows from top to bottom.
>
> if文とforループを関数外で使用できないため、制御の流れがトップダウンになります。

https://github.com/google/starlark-go/blob/master/doc/spec.md#module-execution

# load

Starlarkには `import` がありません。代わりに、 `load` で別ファイルを読み込みます。

```python:my_dict.star
d = {"a": 1}
d["a"] = 2
```

```python:load.star
# from my_dict import d と同じ
# (第2引数は省略不可)
load("my_dict.star", "d")
print(d)
```

```
$ starlark load.star
{"a": 2}
```

# モジュール読み込みとfreeze

別ファイル（モジュール）から読み込んだ値は**破壊的変更を加えられません**。

```python:load.star
load("my_dict.star", "d")
d["a"] = 3
```

```
$ starlark load.star
Traceback (most recent call last):
  load.star:2:2: in <toplevel>
Error: cannot insert into frozen hash table
```

各モジュールが別スレッドで起動するため、競合するのを防ぐためです。

> Each Starlark file defines a module, which is a mapping from the names of global variables to their values. > When a Starlark file is executed, whether directly by the application or indirectly through a load statement, a new Starlark thread is created, and this thread executes all the top-level statements in the file. 
>
> Starlarkの各ファイルはモジュール（グローバル変数の名前とその値を対応付けたもの）を定義します。
> Starlarkファイルが実行されると、アプリケーションから直接実行されるか `load` 文により間接的に実行されるかに関わらず、新たなStarlarkスレッドが作成され、ファイル中のトップレベルのすべての文を実行します。

> If execution reaches the end of the file, module initialization is successful. At that point, the value of each of the module's global variables is frozen, rendering subsequent mutation impossible. 
>
> 実行箇所がファイル末尾に到達すると、モジュールの初期化が成功します。この時点で、モジュールの各グローバル変数は**フリーズされ、後続の破壊的変更の実行が不可能になります**。

https://github.com/google/starlark-go/blob/master/doc/spec.md#module-execution

# エラーハンドリングがない

`try...except` が無いため、エラーが起きたら拾えません。

# クラスがない

クラスも使用できません。言語仕様をシンプルにするためと思われます。

> Starlark is intended not for writing applications but for expressing configuration: its programs are short-lived and have no external side effects and their main result is structured data or side effects on the host application. As a result, Starlark has no need for classes, exceptions, reflection, concurrency, and other such features of Python.
>
> Starlarkはアプリケーションの記述ではなく設定の表現に利用されることが想定されています：プログラムは短命で、外部に副作用を及ぼさず、得られる結果は構造化されたデータかホストのアプリケーションの副作用です。結果として、Starlarkにはクラス、例外、リフレクション、並行処理及びその他のPythonの機能は必要ありません。

https://github.com/google/starlark-go/blob/master/doc/spec.md#overview

# その他

リファレンスに、以下の予約語に対応する構文が無いという[記述](https://github.com/google/starlark-go/blob/master/doc/spec.md#lexical-elements)があります。
(「将来のために残している」とも書かれているので、今後実装されるかもしれません)

```
as             finally        nonlocal
assert         from           raise
class          global         try
del            import         with
except         is             yield
```

# おわりに

以上、StarlarkとPythonの違いの紹介でした。要点さえ押さえれば、普通にPythonと同じように書けそうです。

コマンドのインストールも簡単なので、Pythonのインタープリターの代わりに入れておくのも良いかもしれません。