Open3

Rustベースで開発されているRoc言語について調べてみた #2

eisukeeisuke

Rocレポジトリ内にあるFAQを日本語でまとめてみた

以下がリンク
https://github.com/roc-lang/roc/blob/main/FAQ.md

由来

名前の由来は?
 - Rocは神話上の巨大な鳥から名前が付けられている。

・ロゴはどうして折り紙の鳥?
 - Elm言語のタングラムロゴへのオマージュで、折り紙の鳥を選んだ。
Rocのロゴ

Elmのロゴ

・RocとElmの関係は?
 - RocはElmの直接の子孫であり、類似点があるが同じではない。
https://github.com/elm/compiler

・ロゴのデザインの由来は?
 - 三角形で構成されているのは、コンピュータグラフィックスの基本的なプリミティブであるため。

プリミティブとは、コンピューターグラフィックスやプログラミングにおいて、基本的な要素や単位を指します。プリミティブは、より複雑な形状や構造を作成するための基盤となるシンプルな構成要素です。

例えば、コンピューターグラフィックスにおいて、三角形は非常に一般的なプリミティブです。三角形はシンプルで計算が容易であり、それらを組み合わせることで、さまざまな複雑な形状を表現することができます。そのため、三角形は3Dモデリングやレンダリングにおいて重要な役割を果たしています。

・名前のスペルのバリエーションは?
 - Roc(伝統的)
 - roc(ローキー)
 - ROC(叫ぶ)
 - Röc(メタル🤘)

・面白い事実
 - "roc"は中国語で「鵬」と翻訳され、大きな神話上の鳥を意味する。

なぜ既存のエディタ用プラグインではなく、新しいエディタを作るのか?

Rocエディタは革新的なアイデアを実現する主要な部分であり、既存のエディタ用プラグインに制約されると革新の可能性が大幅に制限されるため。

####予定など
ライブラリとともに出荷されるプラグインの使用がエディタの重要な要素であり、正規表現の可視化やパーサデバッガ、カラーピッカーなどを提供することを想定。

ライブラリ作者にとって、Rocでプラグインを書くことが最も便利であるが、例えばVSCodeでRocで書かれたプラグインを動的に読み込むことは難しいと考えられる

Vim/Emacs/VSCode用のシンタックスハイライトやLSPはあるのか?

現在は存在しないが、将来的には存在することになる。Rocの初期段階では、他のエディタへのサポートを追加するのではなく、Rocエディタに焦点を当てる意識的な取り組みがある。

なぜRocエディタは.md、.gitignore、.ymlなどの非Rocファイルを編集できないのか?

.roc以外のファイルをサポートするデメリットは、スコープの広がりを防ぐことが非常に難しくなること。

例えば、jsonをプレーンテキストで編集し始めると、シンタックスハイライトがなくて不便なので、json用のシンタックスハイライトを追加しようとする。その後、tomlや.mdなどもサポートしたくなり、それらすべてに対してカスタムシンタックスハイライトルールを指定する方法を追加する必要がある。

さらに、オンラインからシンタックスハイライトルールをコピペしたくない人が増えるため、エディタ用のサードパーティ製プラグインマネージャが登場する可能性がある。そして、シンタックスハイライトをエディタの正式な機能として共有できるようになる。しかし、.jsonや.yamlを使っている人たちが.cssも使い始めると、シンタックスハイライトだけでは不便で、エラー報告や無効なセレクターやインポートを入力したときに通知がないので、すぐにRocエディタがVSCodeの難しい部分をすべて実行するような要求が出てくる。(#1 JSの過小評価された点。)

どこで線を引くべきかは難しい問題だが、.rocファイルで明確に線を引くことが最もシンプルであると考えられる。これにより、Rocエディタは.rocファイルの編集に最適なエディタとなり、他のファイルの編集には弱くならない。また、スコープが明確になる。

なぜモジュール内のすべての要素をインポートする方法がないのか?

Elmでは、モジュールをインポートする際に、そのモジュールが公開するすべての要素をスコープに取り込むことができる。しかし、この機能にはデメリットもある。

Rocがこの機能を持たない一つの理由は、特に複数のインポートが使用されている場合、エディタの外部(例えばウェブサイト上)で、どこから要素が来ているかを特定することが難しくなるため。

主な理由はコンパイラのパフォーマンスに関係しており、現在の名前解決ステップでは、モジュール間で並列化が可能。しかし、"すべてを公開する"機能が許可されている場合、すべての"すべてを公開する"モジュールが処理されるまで、何が名前のエラーなのかわからなくなる。そのため、Rocではこの機能がなく、すべてのモジュールで名前解決を並列化できる。

関数を==演算子で等価性を比較できない理由は?

関数の等価性は、一般的な場合には停止問題のために決定不能であることが証明されている。そのため、人間が\x -> x + 1 と \x -> 1 + x を等価だと判断できるかもしれないが、一般的な場合にはコンピュータがこれを信頼性のある方法で行うことはできない。

関数の等価性を定義する他の方法もあるが、それらも問題を抱えている。

一つの方法は、関数のソースコードが等価である場合に二つの関数が等しいとみなす方法。しかし、この定義によって、関数の内部を修正することが安全でなくなる。
もう一つの方法は、参照等価性を用いる方法。これはJavaScriptが行っている方法である。ただし、Rocは他の場所で参照等価性を使用しておらず、関数を引数として渡す場合と、別の場所で関数を定義してから引数として渡す場合で、結果が異なってしまう。
これらの方法は、コードの改訂をリスクにさらすことになるため、避けられるべきである。

もう一つの方法は、関数の等価性が常にfalseを返すように定義することです。この場合、以下の式がどちらもfalseになります。

(\x -> x + 1) == (\x -> 1 + x)
(\x -> x + 1) == (\x -> x + 1)

この方法では関数の等価性は実質的に無意味になりますが、それでも技術的には許可されています。ただし、いくつかの問題があります。

関数をレコードの中に入れると、そのレコードに対して==を使用すると型チェックが通るものの、結果としてfalseが返されます。これは、誤って関数を入れてしまったことに気づかず、バグが発生する可能性があります。

関数(または関数を含む値)をDictやSetに入れると、二度と取り出すことができません。これはNaNのようなもので、NaNも自分自身と等しくないと定義されています。
関数の等価性をコンパイル時に禁止することで、これらの問題をすべて解消することができます。

関数の等価性をコンパイル時に禁止することで、これらの問題をすべて解消することができます。

ロック言語がMaybe, Option, Optional型やnull, nil, undefinedを持っていない理由:

ロック言語は、代わりに標準ライブラリのデータ型を持ちます。
エラーが発生する可能性がある関数は、Result型を使用してエラーを処理します。
Optional Record Fieldという言語機能を使用して、オプションのレコードフィールドを扱います。
何もしないタグユニオンは、Maybeのようなものよりも説明的です。
ロック言語が高位多態性や任意のランク型を持っていない理由:

ロック言語の型システムは、決定可能な主要型推論を持ちます。
高位多態性や任意のランク型をサポートする型システムは、主要型推論が決定不能になります。
コンパイラが型を推論できない状況が生じるため、型アノテーションが必要になります。
高位型についての追加情報:

ロック言語は、ランク1型システムを使用します。

高位型のサポートは、いくつかの重要な欠点があります。
主要な決定的型推論が失われます。
言語の複雑さが増します。
コンパイラのエラーメッセージがわかりにくくなることがあります。
型チェッカーの複雑さが増し、それに伴い速度が低下します。
ランタイムの速度が低下する可能性があります。

これらの理由から、ロック言語はランク1型を維持する予定です。
高次多態性についての追加情報:

ロック言語は、高次多態性をサポートすることはありません。

高次多態性の利点と欠点を理解し、ロック言語においては欠点が利点を上回ると判断しています。

高次多態性のサポートは文化的な影響も考慮する必要があります。

ロック言語の文化とエコシステムを構築するため、高次多態性を持たないことを明確にすることが重要です。

プログラマー間で高次多態性についての意見が分かれていることがあります。

高次多態性をサポートするかどうかに関してプログラミング言語は中立的ではありません。

言語デザイナーは次の3つの選択肢があります:
標準ライブラリでHKPとMonadを持つ
標準ライブラリでHKPを持つがMonadを持たない
HKPを持たない

ロック言語においては、技術的および文化的な面から、HKPをサポートしないことが最善の選択であると判断されています。

結論:
ロック言語は、MaybeやOption、Optional型、null、nil、undefinedなどを持たず、高位多態性や任意のランク型もサポートしません。これは、型システムの決定可能性、言語の複雑さ、コンパイラエラーの理解可能性、型チェッカーのパフォーマンス、ランタイム速度などの観点から判断されています。また、高次多態性のサポートは文化的な影響も考慮に入れる必要があります。ロック言語は、技術的および文化的な面から、高次多態性をサポートしないことが最善の選択であると判断されています。

eisukeeisuke

高次多態性とは

高次多態性とは、プログラミング言語の型システムにおける概念で、関数が引数や返り値として別の関数を取ることができるという性質を指します。これらの関数は、型を引数として受け取り、異なる型で動作することができる新たな関数を返すことができます。この特性は、関数型プログラミング言語でよく見られ、抽象化やコードの再利用性を高める助けとなります。

たとえば、Haskellのような高度に多態的な言語では、関数の引数として型を取り、その型で動作する新しい関数を返すことができます。これにより、非常に抽象的なコードを書くことが可能となり、同じ関数をさまざまな型で再利用することができます。

しかし、高次多態性は型システムとコンパイラの複雑さを増すため、すべての言語がこれをサポートするわけではありません。高次多態性をサポートしない言語では、型システムの決定可能性を維持し、ランタイムのパフォーマンスを最適化し、エラーメッセージの理解可能性を向上させることができます。

【寄り道】Haskellサンプルコード

若干Haskellの入門になります。高次多態性について説明しやすいため書いておきます。

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

実際には宣言したものを以下のように使う

map (*10) [1, 2, 3]      -- 結果は [10, 20, 30]
map (*10) [1.5, 2.5, 3.5]  -- 結果は [15.0, 25.0, 35.0]

このコードは...

  • マップ関数の型宣言
    • (引数(引数ー>戻り値))ー>引数ー>戻り値、という構成になっています
  • マップ関数に条件を追加
    • 空の配列が来たら空の配列を返すように設定しています。
  • 型宣言に合わせて関数を宣言
    • : (コロン)は引数の配列がどこからどこまでを指定するためのものです。([a]の要素のxからxsまでということを示している)

どのような処理が行われているのかというと...
引数aと[a]の各要素を掛け合わせてbを返していてそれを[b]に格納しているような感じになっている。

結論として高次多態性とは

map f (x:xs) = f x : map f xs

以上のコードのように関数が引数として別の関数や型を受け取り、または返す能力のことです。

普段はc#を主に触っていてジェネリクスとかが高次多態性に近いものかなと思いました。

eisukeeisuke

関数の等価性をコンパイル時に禁止についてC#で表してみた

関数 = 関数 //これができないということ 

c#で表すと

public class Sample 
{
            public bool A(){
                        return true;
            }
            
            public bool B(){
                        return true;
            }

            public void main(){
                        A() = B(); //このようなことがRocでは禁止されている
            }
}