静的に型付けされた完全なLispバリアントは可能ですか?


107

静的に型付けされた完全なLispバリアントは可能ですか?このようなものが存在することは理にかなっていますか?Lisp言語の利点の1つは、その定義の単純さです。静的型付けはこのコアの原則を危険にさらすでしょうか?


10
私はLispのフリーフォームマクロが好きですが、Haskellの型システムの堅牢性が好きです。静的に型付けされたLispがどのように見えるかを見たいです。
mcandre

4
良い質問!shenlanguage.orgそうしていると思います。もっと主流になってほしいです。
Hamish Grubijan 2013


Haskellでシンボリックコンピューティングをどのように行いますか?( 'x'(=(+ xy)(* xy)))を解きます。それを文字列に入れると、チェックはありません(マクロを使用してチェックを追加できるLispとは異なります)。代数的データ型またはリストを使用する場合...非常に冗長になります:solve(Sym "x")(Eq(Plus(Sym "x")(Sym "y"))(Mult(Sym "x") (Sym "y")))
aoeu256 '

回答:


57

はい、それは非常に可能ですが、標準のHMスタイルの型システムは通常、ほとんどの慣用的なLisp / Schemeコードでは間違った選択です。静的型付けを備えた「フルLisp」(実際にはSchemeに似ています)である最近の言語については、型付きラケットを参照してください。


1
ここでの問題は、型付きラケットプログラムのソースコード全体を構成するリストの種類は何ですか?
Zorf

18
それは通常でしょうSexpr
Eli Barzilay

しかし、私coerce :: a->bはevalの観点から書くことができます。タイプセーフはどこにありますか?
ssice

2
@ssice:型指定されていない関数を使用している場合eval、結果をテストして結果を確認する必要がある場合、これはTyped Rackedの新機能ではありません(Stringおよびの共用体型を取る関数と同じNumberです)。これ実行できることを暗黙的に確認する方法は、動的に型付けされた言語をHM静的に型付けされた言語で記述して使用できることです。
Eli Barzilay 2016年

37

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コード自体の変換に非常に優れています。

では、このような構造を静的に型付けされた言語でどのようにモデル化するのでしょうか。非常に強力で正確な静的型システムを持つHas​​kellで試してみましょう。

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が採用したアプローチです。


16
この答えには根本的な問題があります。静的型システム HMスタイルである必要があると想定しています。そこでは表現できず、Lispコードの重要な機能である基本概念はサブタイプ化です。タイプされたラケットを見てみる(Listof Integer)と、やなどのあらゆる種類のリストを簡単に表現できることがわかります(Listof Any)。明らかに、タイプについて何も知らないので、後者は役に立たないと思われますが、TRでは後で使用でき(if (integer? x) ...)、システムはそれxが第1ブランチの整数であることを認識します。
Eli Barzilay、2010

5
ああ、それは型付きラケットの悪い特徴です(これは、いくつかの場所で見られるような不健全な型システムとは異なります)。型付きラケットがある静的型付け型付けされたコードのための無実行時のオーバーヘッドで、言語。ラケットでは、一部のコードをTRで記述したり、一部のコードを通常の型なし言語で記述したりすることができます。これらの場合、コントラクト(動的チェック)を使用して、型付きのコードが誤動作する可能性のある型なしコードから保護されます。
Eli Barzilay、2010

1
@Eli Barzilay:私が嘘をついた、4つの部分があります:4.業界で認められているC ++コーディングスタイルがサブタイプからジェネリックに徐々に移行しているのは興味深いことです。弱点は、ジェネリック関数が使用するインターフェイスを宣言するためのヘルプが言語で提供されていないことで、型クラスが確実に役立つものです。さらに、C ++ 0xは型推論を追加している可能性があります。HMではない、と私は思いますが、その方向に忍び寄りますか?
オーウェンS.

1
オーウェン:(1)重要な点は、lispersが書き込むコードの種類を表すにはサブタイプが必要であり、HMシステムではそれができないため、使用ごとにカスタムタイプとコンストラクターを使用する必要があります。全部を使うのはずっと厄介です。サブタイプのシステムを使用した型付きラケットでは、意図的な設計決定の結果でした。結果は、コード変更したりカスタムタイプを作成したりせずに、そのようなコードのタイプを表現できるはずです。
Eli Barzilay、2010

1
(2)はい、dynamicタイプは、動的に型付けされた言語の利点のいくつかを得る一種の回避策として静的言語で一般的になりつつあり、これらの値の通常のトレードオフは、タイプを識別可能にする方法でラップされています。しかし、ここでも型付けされたラケットは、言語内でそれを便利にするのに非常に優れています。型チェッカーは、述語の出現を使用して型について詳しく知ることができます。たとえば、ラケットページで入力した例を参照し、string?文字列と数値のリストを文字列のリストに「縮小」する方法を確認してください。
Eli Barzilay、2010

10

私の答えは、高い自信がなければおそらくです。たとえばSMLのような言語を見て、それをLispと比較すると、それぞれの機能コアはほとんど同じです。その結果、なんらかの静的型付けをLispのコア(関数アプリケーションとプリミティブ値)に適用するのにそれほど問題がないように思われます。

あなたの質問は十分に述べていますが、私が直面している問題のいくつかがデータとしてのコードのアプローチです。型は、式よりも抽象的なレベルで存在します。Lispにはこの区別はありません-すべてが「フラット」な構造です。式E:T(Tはその型の一部の表現)を検討し、この式をプレーンなデータであると見なす場合、ここでのTの型は何ですか?まあ、それは一種です!種類はより高い注文タイプなので、先に進んで、コードでそれについて何か言いましょう。

E : T :: K

これでどこへ行くのかわかるかもしれません。コードから型情報を分離することで、この種の型の自己参照を回避することができると確信していますが、その場合、型のフレーバーがあまり「不自然」になりません。これを回避するには多くの方法がありますが、どれが最適かはわかりません。

編集:ああ、それで少しグーグルで、私はそれが静的に型付けされていることを除いてLispに非常に似ているように見えるQiを見つけました。おそらく、静的型付けを行うために変更が加えられた場所を確認するのに良い場所でしょう。


QiがShenの次のイテレーションで、同じ人が開発したようです。
ダイアゴン

4

リンクが切れています。しかし、いずれにせよ、ディランは静的に型付けされていません。
ビョルンLindqvist

@BjörnLindqvist:そ​​のリンクはディランに漸進的なタイピングを追加することに関する論文へのリンクでした。
Rainer Joswig、

1
@BjörnLindqvist:概要論文にリンクしました。
Rainer Joswig、

ただし、段階的型付けは静的型付けとは見なされません。もしそうなら、段階的な型付けも使用するため、Pypyは静的に型付けされたPythonになります。
ビョルンLindqvist

2
@BjörnLindqvist:段階的な型付けによって静的型を追加し、これらがコンパイル中にチェックされる場合、これは静的型付けです。プログラム全体が静的に型付けされているだけでなく、パーツ/リージョンもそうです。homes.sice.indiana.edu/jsiek/what-is-gradual-typing「段階的な型付けは、2006年にWalid Tahaと一緒に開発した型システムで、プログラムの一部を動的に型付けしたり、他の部分を静的に型付けしたりできます。」
Rainer Joswig、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.