静的に型付けされた完全なLispバリアントは可能ですか?このようなものが存在することは理にかなっていますか?Lisp言語の利点の1つは、その定義の単純さです。静的型付けはこのコアの原則を危険にさらすでしょうか?
静的に型付けされた完全なLispバリアントは可能ですか?このようなものが存在することは理にかなっていますか?Lisp言語の利点の1つは、その定義の単純さです。静的型付けはこのコアの原則を危険にさらすでしょうか?
回答:
はい、それは非常に可能ですが、標準のHMスタイルの型システムは通常、ほとんどの慣用的なLisp / Schemeコードでは間違った選択です。静的型付けを備えた「フルLisp」(実際にはSchemeに似ています)である最近の言語については、型付きラケットを参照してください。
Sexpr
。
coerce :: a->b
はevalの観点から書くことができます。タイプセーフはどこにありますか?
eval
、結果をテストして結果を確認する必要がある場合、これはTyped Rackedの新機能ではありません(String
およびの共用体型を取る関数と同じNumber
です)。これが実行できることを暗黙的に確認する方法は、動的に型付けされた言語をHM静的に型付けされた言語で記述して使用できることです。
Lispのように見える静的に型付けされた言語が必要な場合は、言語を表す抽象構文ツリーを定義し、そのASTをS式にマッピングすることで、比較的簡単に行うことができます。しかし、結果をLispと呼ぶことはないと思います。
構文のほかに実際にLisp-yの特性を持つものが必要な場合は、静的に型付けされた言語でこれを行うことができます。しかし、Lispには多くの便利な静的型付けを実現するのが難しい特徴があります。説明のために、Lispの主要な構成要素であるconsと呼ばれるリスト構造自体を見てみましょう。
consをリストと呼ぶことは、リストの(1 2 3)
ように見えますが、少し間違っています。たとえば、C ++ std::list
やHaskellのリストなど、静的に型付けされたリストとはまったく同じではありません。それらは、すべてのセルが同じタイプである1次元のリンクリストです。Lispは喜んで許可します(1 "abc" #\d 'foo)
。さらに、静的型付きリストを拡張してリストのリストをカバーする場合でも、これらのオブジェクトのタイプでは、リストのすべての要素がサブリストである必要があります。((1 2) 3 4)
それらをどのように表現しますか?
Lispコンスは、葉(アトム)とブランチ(コンス)を持つバイナリツリーを形成します。さらに、そのようなツリーの葉には、アトミック(非cons)Lispタイプがまったく含まれている可能性があります。この構造の柔軟性により、Lispは記号計算、AST、およびLispコード自体の変換に非常に優れています。
では、このような構造を静的に型付けされた言語でどのようにモデル化するのでしょうか。非常に強力で正確な静的型システムを持つHaskellで試してみましょう。
type Symbol = String
data Atom = ASymbol Symbol | AInt Int | AString String | Nil
data Cons = CCons Cons Cons
| CAtom Atom
最初の問題は、Atomタイプのスコープです。明らかに、私たちがAtomタイプを選んだのは、私たちがコンスに振り回したいすべてのタイプのオブジェクトをカバーするのに十分な柔軟性を持っているわけではありません。上記のAtomデータ構造を拡張しようとするのではなく(これは明らかに脆弱であることがわかります)、Atomic
アトミックにしたいすべての型を区別する魔法の型クラスがあるとします。それから私たちは試すかもしれません:
class Atomic a where ?????
data Atomic a => Cons a = CCons Cons Cons
| CAtom a
しかし、ツリー内のすべての原子が同じタイプである必要があるため、これは機能しません。私たちは、彼らが葉ごとに異なることができるようにしたいです。より優れたアプローチでは、Haskellの存在量指定子を使用する必要があります。
class Atomic a where ?????
data Cons = CCons Cons Cons
| forall a. Atomic a => CAtom a
しかし今、あなたは問題の核心に来ます。このような構造の原子で何ができるでしょうか?モデル化できる共通の構造は何Atomic a
ですか?そのようなタイプでは、どのレベルのタイプセーフが保証されますか?タイプクラスに関数を追加していないことに注意してください。また、正当な理由があります。アトムはLispで共通するものを何も共有していません。Lispでのそれらのスーパータイプは単に呼ばれますt
(すなわちトップ)。
それらを使用するには、アトムの値を実際に使用できるものに動的に強制するメカニズムを考案する必要があります。そして、その時点で、静的型付けされた言語内に動的型付けされたサブシステムを基本的に実装しました!(グリーンスパンの第10のプログラミングの規則の可能な帰結に気づかざるを得ません。)
Haskellはちょうどそのようなのためのサポートを提供することを注意動的なサブシステムをとObj
と一緒に使用される型、Dynamic
種類及びTypeableクラス当社置き換えるためにAtomic
任意の値がその型で保存することを可能にするクラスを、そしてそれらの型からの明示的な強制バック。これは、Lispのcons構造体を完全な一般性で操作するために使用する必要がある種類のシステムです。
また、他の方法で、静的に型付けされたサブシステムを本質的に動的に型付けされた言語に埋め込むこともできます。これにより、より厳密な型要件を利用できるプログラムの部分の静的型チェックの利点が得られます。これは、たとえばCMUCLの限定された形式の正確な型チェックで採用されているアプローチのようです。
最後に、動的および静的に型付けされた2つの別個のサブシステムがあり、コントラクトスタイルのプログラミングを使用して、2つのサブシステム間の遷移をナビゲートする可能性があります。こうすることで、静的型チェックがヘルプよりも邪魔になるLispの使用法や、静的型チェックが有利になる使用法に対応できます。以下のコメントからわかるように、これはTyped Racketが採用したアプローチです。
(Listof Integer)
と、やなどのあらゆる種類のリストを簡単に表現できることがわかります(Listof Any)
。明らかに、タイプについて何も知らないので、後者は役に立たないと思われますが、TRでは後で使用でき(if (integer? x) ...)
、システムはそれx
が第1ブランチの整数であることを認識します。
dynamic
タイプは、動的に型付けされた言語の利点のいくつかを得る一種の回避策として静的言語で一般的になりつつあり、これらの値の通常のトレードオフは、タイプを識別可能にする方法でラップされています。しかし、ここでも型付けされたラケットは、言語内でそれを便利にするのに非常に優れています。型チェッカーは、述語の出現を使用して型について詳しく知ることができます。たとえば、ラケットページで入力した例を参照し、string?
文字列と数値のリストを文字列のリストに「縮小」する方法を確認してください。
私の答えは、高い自信がなければおそらくです。たとえばSMLのような言語を見て、それをLispと比較すると、それぞれの機能コアはほとんど同じです。その結果、なんらかの静的型付けをLispのコア(関数アプリケーションとプリミティブ値)に適用するのにそれほど問題がないように思われます。
あなたの質問は十分に述べていますが、私が直面している問題のいくつかがデータとしてのコードのアプローチです。型は、式よりも抽象的なレベルで存在します。Lispにはこの区別はありません-すべてが「フラット」な構造です。式E:T(Tはその型の一部の表現)を検討し、この式をプレーンなデータであると見なす場合、ここでのTの型は何ですか?まあ、それは一種です!種類はより高い注文タイプなので、先に進んで、コードでそれについて何か言いましょう。
E : T :: K
これでどこへ行くのかわかるかもしれません。コードから型情報を分離することで、この種の型の自己参照を回避することができると確信していますが、その場合、型のフレーバーがあまり「不自然」になりません。これを回避するには多くの方法がありますが、どれが最適かはわかりません。
編集:ああ、それで少しグーグルで、私はそれが静的に型付けされていることを除いてLispに非常に似ているように見えるQiを見つけました。おそらく、静的型付けを行うために変更が加えられた場所を確認するのに良い場所でしょう。