web-dev-qa-db-ja.com

scalaが依存型を明示的にサポートしない理由は何ですか?

パスに依存するタイプがあり、ScalaでEpigramやAgdaなどの言語のほとんどすべての機能を表現することは可能だと思いますが、なぜScalaは this 他の領域(DSLなど)で非常にうまく機能するように、より明示的に?「必要ない」など、私が見逃しているものは何ですか?

101

(経験から知っているように、依存型を完全にサポートしているが非常に便利な方法ではないCoq証明アシスタントで使用しているため)依存型は非常に高度なプログラミング言語機能であるため、正しく-実際には複雑さの指数関数的な爆発を引き起こす可能性があります。彼らはまだコンピューターサイエンスの研究のトピックです。

5
Robin Green

Scalaのパス依存型はΣ型のみを表現でき、Π型は表現できないと思います。この:

trait Pi[T] { type U }

exactly型ではありません。定義により、Π型、つまり従属積は、結果の型が引数値に依存する関数であり、汎用数量詞、つまり∀x:A、B(x)を表します。ただし、上記の場合、タイプTのみに依存しますが、このタイプの値には依存しません。 Pi特性自体は、Σタイプ、実存量指定子、つまり∃x:A、B(x)です。この場合のオブジェクトの自己参照は、数量化変数として機能しています。ただし、暗黙的なパラメーターとして渡されると、型ごとに解決されるため、通常の型関数になります。 Scalaの依存製品のエンコーディングは次のようになります。

trait Sigma[T] {
  val x: T
  type U //can depend on x
}

// (t: T) => (∃ mapping(x, U), x == t) => (u: U); sadly, refinement won't compile
def pi[T](t: T)(implicit mapping: Sigma[T] { val x = t }): mapping.U 

ここで不足している部分は、フィールドxを期待値tに静的に制約する能力であり、タイプTに属するすべての値のプロパティを表す方程式を効果的に形成します。論理が形成され、その方程式は証明される定理です。

余談ですが、実際の場合、定理は、コードから自動的に導出したり、多大な労力なしで解決したりできない点まで、非常に自明ではありません。リーマン仮説をこのように定式化することもできますが、実際に証明したり、永遠にループしたり、例外をスローしたりせずに署名を実装することは不可能です。

2
P. Frolov

質問は、依存型付けされた機能をより直接的に使用することに関するものであり、私の意見では、Scalaが提供するものよりも直接的な依存型付けアプローチを持つことには利点があるでしょう。
現在の回答では、タイプ理論レベルでの質問を議論しようとしています。もっと実用的なスピンをしたいです。これは、Scala言語で依存型のサポートのレベルで人々が分割される理由を説明するかもしれません。私たちは多少異なる定義を念頭に置いているかもしれません。 。

これは、Scala= Idrisのようなもの(非常に難しいと思います)に変えるのがどれほど簡単かという質問に答えようとしたり、Idrisのような機能をより直接サポートするライブラリを記述しようとするものではありません( singletonsのようにHaskellに入れようとします)。

代わりに、ScalaとIdrisのような言語との実用的な違いを強調したいと思います。
値および型レベルの式のコードビットとは何ですか? Idrisは同じコードを使用します。Scalaは非常に異なるコードを使用します。

Scala(Haskellと同様)は、多くのタイプレベルの計算をエンコードできる場合があります。これは、shapelessなどのライブラリによって示されます。これらのライブラリは、いくつかの本当に印象的で巧妙なトリックを使用して実行します。ただし、それらのタイプレベルのコードは、(現在)値レベルの式とはまったく異なります(Haskellではギャップがいくぶん近くなることがわかります)。 Idrisでは、タイプレベルの現状のままで値レベルの式を使用できます。

明らかな利点は、コードの再利用です(両方の場所で必要な場合、値レベルとは別に型レベルの式をコーディングする必要はありません)。値レベルのコードを書く方が簡単なはずです。シングルトンのようなハッキングに対処する必要がない方が簡単です(パフォーマンスコストは言うまでもありません)。 1つのことを学ぶ2つのことを学ぶ必要はありません。実用的なレベルでは、必要な概念が少なくなります。型の同義語、型族、関数、...関数だけではどうですか?私の意見では、この統一された利点ははるかに深く、構文上の利便性以上のものです。

検証済みのコードを検討してください。見る:
https://github.com/idris-lang/Idris-dev/blob/v1.3.0/libs/contrib/Interfaces/Verified.idr
型チェッカーは、モナド/ファンクター/適用法の証明を検証し、その証明は、モナド/ファンクター/適用の実際の実装に関するものであり、同じまたは異なる可能性のあるエンコードされた型レベルの同等物ではありません。大きな問題は、私たちが何を証明しているかということです。

巧妙なエンコーディングトリックを使用しても同じことができます(Haskellバージョンについては以下を参照してください。Scalaについては見ていません)。
https://blog.jle.im/entry/verified-instances-in-haskell.html
https://github.com/rpeszek/IdrisTddNotes/wiki/Play_FunctorLaws
型が非常に複雑で法則が見にくい場合を除き、値レベルの式は(自動的に、しかしまだ)型レベルのものに変換され、その変換も信頼する必要があります。このすべてにエラーの余地があり、これはコンパイラーが証明アシスタントとして機能するという目的をやや否定します。

(編集済み2018.8.10)実証支援について話すと、IdrisとScalaのもう1つの大きな違いがあります。 Scala(またはHaskell)には、発散証明の記述を防ぐことができるものは何もありません。

case class Void(underlying: Nothing) extends AnyVal //should be uninhabited
def impossible() : Void = impossible()

一方、イドリスにはtotalキーワードがあり、このようなコードはコンパイルできません。

Scala値と型レベルのコードを統一しようとするライブラリ(Haskell singletonsなど)は、Scalaが依存型をサポートするための興味深いテストになります。 in Scalaパス依存型のため?

私はScalaにあまりにも新しいので、自分でその質問に答えることはできません。

0
robert_peszek