グレムリンのための言語、Raku
(buttondown.email)- Rakuは、Python、J、Frink、Excelの数学作業における隙間を埋める電卓言語の候補として検討され、短い実験だけでも非常に強力だが奇妙な言語という印象を与えた
- Unicode記号、英数字の中置演算子、リスト積・zip・リデュース・累積、
~~マッチャー、...シーケンスのように、演算子中心の表現力が広い - ユーザーは中置演算子だけでなく、circumfix/postcircumfix演算子まで定義でき、結合性も左・右結合を超えてチェーン結合・リスト結合まで指定できる
- マルチディスパッチは型シグネチャだけでなく、
whereのランタイム述語でも分岐し、関数シグネチャとその中のパラメータも第一級の値として扱える - 大規模コードベースの保守には負担が大きそうだが、使い捨てスクリプト・計算・個人用ツールのような小規模プログラミングには魅力的で、ドキュメント、WindowsのREPL、コンパイル速度、sigilのミスが主な障壁になっている
Rakuを見てみたきっかけと第一印象
- RakuはかつてPerl 6として知られていた言語である
- 動的言語への不満を書いた後、何人かのユーザーがRakuを勧めてきたため、数学作業に使う電卓言語として適しているかを確かめるために見てみた
- これまではPython、J、Frink、Excelを組み合わせて使っていたが、それぞれに大きな欠点があった
- 数日間試した後の印象は、「本当に賢いグレムリンたちが、ほかのグレムリンたちから大量のフィードバックを集めて設計した言語」に近かった
独特な演算子体系
- RakuはUnicode演算子を積極的に使う
- 集合に含まれるかどうかは
∈で確認する ∉、∋、∌もある
- 集合に含まれるかどうかは
- 英数字の中置演算子も許可している
- 文字列の繰り返し演算子は
x - 関数合成は
o
- 文字列の繰り返し演算子は
- リストの組み合わせも短い記号で表現する
Xはリストの直積を作るXfは直積の各要素にfを適用するZfはzip方式で同じことを行う
- 中置演算子
fに対して、[f]はリストをリデュースし、[\f]は累積結果を作る[+] <1 2 3 4 5>は15[\+] <1 2 3 4 5>は(1 3 6 10 15)
~~マッチャーと...シーケンス
~~は複数種類の比較を1つの構文で扱うマッチャーとして使われる"abc" ~~ "abc"は文字列の一致を確認する"abc" ~~ Strは文字列型かどうかを確認する"abc" ~~ {.chars == 3}は長さが3かどうかを検査する"abc" ~~ /^b/はabcがbで始まるかを確認する
...は前の値からパターンを捉えてシーケンスを作る0,1,2...10は0から10まで1ずつ増える0,2,4...10は偶数シーケンスになる1,2,4...10は1 2 4 8のような増加パターンに従う
ユーザー定義演算子
- Rakuは一部の言語のように中置演算子を定義できるだけにとどまらず、circumfixとpostcircumfix演算子も作れる
- たとえば
sub circumfix:<[∀ zz>($inner){sum($inner)}のように囲み演算子を定義し、内部の値を合計できる - ベクトルの内積のように見えるpostcircumfix演算子も定義可能である
sub postcircumfix:<| ⟩>(@left, @inside){[+] (@left Z* @inside)}<1 2 3>|<4 5 6>⟩は32
- 演算子の結合性も多様に指定できる
- 一般的な左結合・右結合の中置演算子を定義できる
x < y < zがx < y && y < zのように解釈されるチェーン結合を指定できるa op b op cがop(a, b, c)になるリスト結合もサポートする
マルチディスパッチとランタイム条件分岐
- Rakuはマルチディスパッチをサポートし、異なる型シグネチャを持つ複数の関数定義から適切なものを選ぶ
- 例の関数
fは引数の組み合わせに応じて異なる動作をする- スカラーと配列を受け取ると、配列の各要素にスカラーを足す
- 配列とスカラーを受け取る場合もサポートする
- 2つの配列を受け取ると、
Z+で各要素を足す
- さらに独特なのは、値のランタイム述語でもディスパッチできる点である
multi my_abs(Int $x where {$x > 0}) {$x}multi my_abs(Int $x) {-$x}
- 関数のシグネチャは第一級の値であり、シグネチャ内のパラメータも第一級の値である
小さな機能が生む広い表面積
MAIN関数を定義すると、そのパラメータが自動的にCLIフラグに変換される- オブジェクトには、あらかじめ提供されているメソッドが非常に多い
- List objectは、すべての順列、すべてのk-組合せ、すべてのスライディングウィンドウを得るメソッドを提供する
- Junctionsは、一度に複数の比較を行うための独特な値の形式である
1|2はany(1, 2)に展開され、1 < 1|2が成立する1&2はall(1, 2)に展開され、1 !< 1&2が成立する
- どんな中置演算子でも、前に
!を付ければ否定演算子にできる - Rakuは
$kebab-caseという名前と中置の減算を同時に持つ言語に見え、sigilがx-yを区別できるようにしていると推測される - regex syntaxはPerl 5との後方互換性がない
- 30年間、各言語はPCRE「標準」に従ってきたが、Perl 6はそれを捨てた
まだ見ていない領域と小規模での魅力
- 見てきた内容は、電卓用途を中心とした一部の機能にすぎない
- まだオブジェクトシステム、パッケージ、grammarsは学んでいない
- 関数本体で
samewithが同じ関数を新しい引数で再度呼び出す機能のように、抜けている機能も多い - Rakuのレガシーコードベースを保守しなければならないなら非常に大変そうだが、In The Smallプログラミングには強力に見える
- 使い捨てスクリプト
- 計算
- 個人用ツール
- 最初に望んでいた種類の作業
不満と期待
- ドキュメントが非常に不足しており、記号への依存度が高いため検索しにくい
- ドキュメントが不足している言語をいくつも学んだ経験があっても、Rakuはそれよりはるかに大きく複雑なため、モチベーションが下がる可能性がある
- WindowsでREPLにUnicodeを入力するとクラッシュする
- コンパイラもかなり遅く、小さなファイルでも0.5秒以上かかり、反復作業がつらい
- sigil体系が不便である
$xを@xの代わりに書いてしまい、問題をデバッグするのに30分費やした事例がある
- 全体としてRakuは気に入っており成功してほしいが、時間とともにコンパイル時間とドキュメントが改善されることを期待している
1件のコメント
Hacker Newsの意見
プログラミング言語を2次元空間に配置するなら、軸はどれだけ驚かせるかと、驚いたときに楽しいか/ひどいかになり得る
普通の言語はたいてい「ほとんど驚かせないが、たまに驚かせても楽しい」左下にあるべきだと暗黙に期待されるが、Rakuは珍しく空いている左上を堂々と目指している感じがする。「変でしょ? かっこよくない?」という態度に近い
document.write = function ...のようなJavaScriptを書いたことがあり、必要なことは達成できたという点では楽しかったが、同時にかなりひどかったPerlにも「楽しい驚き」が多くあり、Rakuは主にPerlのひどい驚きを取り除こうとした設計だったのだと思う
∈で集合に含まれるかどうかを確認するというくだりで、どういう意味か分かった0,2,4...10は(0 2 4 6 8 10)になるが、1,2,4...10が(1 2 4 8)になるのは「OEISで次の数を探しているのか?」という気分になるRakuは言語としては興味深いが、一部のイディオムが頭に入りにくい
AppleScriptが自然言語のように見せようとして奇妙に感じられたのと同じように、
my、say、sub、gatherのような自然言語風の要素と、@のような記号、モジュール宣言など、外部の人にはビザンチン的に見える構文上の決定が混ざっている。99 bottlesの例も論理的には追えるが、直感的に見つけるのは難しい。記号が多く、文脈によって過負荷される感じがするので、自然言語パーサーのようにRakuが最適そうな作業であっても、自分で使いたいとは思わないhttps://examples.raku.org/categories/module-management/Fletc...
@シジルはかなり見慣れているはずRakuでいちばん好きな機能は、整数除算と10進リテラルがどちらも有理数型である
Ratを返す点誰もが浮動小数点がいまいちだと分かっているが、実際にそこから抜け出そうとする言語はほとんどなく、Rakuでは科学的記数法を使って初めて浮動小数点リテラルになる
これらは有理数や複素有理数まで含む数値階層を持っており、任意精度数も当然サポートしている。組み合わせ可能性は素晴らしい
Ratの表現が大きくなりすぎると自動的に浮動小数点に変わるため1/10はRatだが、1/100000000000000000000はNumになる。昇格されないFatRatもあるが、デフォルトではないだから「不正確な浮動小数点を明示的に要求していないのに使う方式から脱した」というより、そもそもそんな状態だったことがなかったと見るのが正しい
(/ 1.0 3.0)は0.3333333333333333、(/ 1 3)は1/3、(- (+ 0.1 0.2) 0.3)は5.551115123125783e-17になるDecimal()よりfloat()の呼び出しのほうがはるかに多いお金を直接扱う場合でなければ、ほとんど常に浮動小数点が望む選択肢だ
Rakuのドキュメントが「本当にひどい」とは感じなかった。むしろ公式ドキュメントサイトが概念ドキュメントとAPIドキュメントの両方を含むワンストップの資料であることに感心した
https://docs.raku.org/
概念ドキュメントの出発点としては、このページがとても優れている: https://docs.raku.org/language
ある内容の大半はよく書かれていて有用なサンプルコードもあるが、まったく文書化されていなかったり、単純な場合しか扱っていなかったりする部分に時々出会う。特にモジュールシステムが最大の問題で、モジュール/パッケージの区別はModulesページを読むだけでは理解しにくい。インポートされる名前空間と宣言された名前空間が異なり得る一方で、コンパイラが見つけるにはディレクトリ構造が名前空間と一致していなければならない、という点は有用で奇妙に筋が通っているが、実際に触って学ぶ必要があった
RakuのかっこいいUnicode演算子には、すべてASCII代替表記がある
たとえば
∈、∉、∋、∌の代替表記は(elem)、!(elem)、(cont)、!(cont)であるhttps://docs.raku.org/language/unicode_ascii#Other_acceptabl...
言語をよく知る前に出てくる典型的な批判が、今も繰り返されているように思う。昔は Perl の「ラインノイズ」だったし、今では Raku がUnicode 演算子をためらいなく使う、というような話になっている。
とはいえこれは任意で、画面上でコードを小さく表現力豊かに見せるために自分でも使ってみた。慎重で創造的な Unicode の使い方にはよく合っている。シジルが嫌いだという反応もよくあるが、Perl のスイスアーミー・チェーンソーのような表現力が好きで Raku/Perl 6 を使ってきたし、Raku は Perl らしさを二乗したような感じだ。整理されていて表現力が高く、古き良き Perl の上に巨大な機能の山が積み上がっている。ドキュメントも良いが継続的な手入れは必要で、Perl のドキュメントと比べると基準は非常に高い。
数文字を節約するためにこうするのは複雑で、あまり意味がないように見える。
構文糖でいっぱいのプログラミング言語がどんな姿になるのか、ときどき気になっていたが、これで分かった。
「ひどいのに妙に惹かれる、目を離せない、もっと見せてくれ」という感じだ。
GitHub の紹介には “slaps roof of [programming language]* this bad boy can fit so much [syntax sugar] into it.” とある。直近の Advent of Code でもその言語を使って優勝している: https://github.com/betaveros/noulith
Raku がもともと Perl 6 として始まり、多くの設計思想が Perl 的な考え方から来ている点は覚えておく価値がある。
文字列の繰り返し用の
x演算子にすぐ反応しているのを見ると、著者は Perl と Raku の歴史をよく知らないように見えるが、それは Perl では何十年も前からそうだった。理由もなく Perl 6 と呼ばれたわけではないし、ほぼ同じ Perl チームが開発した。Perl 5 をかなり使ったことがある人なら、Perl6/Raku の遺産ははっきり見える。Raku のオブジェクトモデル全体も、CPAN の Perl 5 モジュールである Moose.pm を少し強力にしたバージョンに近い。
正直、Perl 5/PCRE の正規表現構文はひどい。
それが存在する理由も、昔の正規表現構文では
(?が構文エラーだったため、任意の意味に再定義できただけだ。Raku は、正規表現が何を表現すべきか分かった今、最初から正気の正規表現言語を設計しようとする試みだ。代替案は、これからさらに30年(?:this|(?>or that))のようなものに閉じ込められることだ。Perl には長いこと触っていないが、今でも正規表現はよく使う。
ある意味では確かにグレムリンだ。奇妙で複雑だが、生産性を高めてくれる道具が好きだ。
ただし「大きなプログラム vs 小さなプログラム」という比較には同意しない。あまり賢明でない人は、それが大きな作業には悪い言語だという意味に受け取るかもしれないが、実際には他の言語と同じくらい良いか、それ以上かもしれない。問題は他のグレムリン言語と同じく、うまく使うには知恵が必要だということだ。たとえば
$xと@xを混同する人は、似た言語を十分使ってきた人ならほとんどいない。シジルはコードを読むときに変数の単純な型、Raku ではインターフェースをすぐ知らせてくれるので、むしろ楽になるし、同じ変数名前空間を別のシジルで有用に使える。奇妙に見え、不要な文字のように見え、意味を知る必要はあるが、生活を楽にしてくれることがある: https://www.perl.com/article/on-sigils/問題は、自分が何をしているのかよく分かっていない人たちに生じる。そういう人たちにとって、このグレムリン言語は生きた悪夢になり得るし、ボウリングのバンパー、浮き輪、ケブラー手袋、安全ヘルメット、GPS のような保護装置がたくさん必要だ。グレムリン言語で摩天楼を建てられないという意味ではなく、事故を起こしやすい非グレムリンには建てられないだけで、賢いグレムリンなら建てられる。
道具は、チームが必要な仕事をより良く、またはより速くこなせるよう助けるために存在する。ジュニアをふるい落としたり、自分の自尊心を満たしたりするために不必要に複雑な道具を使うなら、その道具は組織の他の人たちにとって助けではなく、武器に近い。