1. ホーム
  2. scala

[解決済み] 依存するメソッドタイプの説得力のある使用例とは?

2022-07-11 18:46:46

質問

以前は実験的な機能であった依存するメソッドタイプは、現在では トランクでデフォルトで有効になりました。 となり、どうやらこれによって を刺激したようです。 を巻き起こしたようです。

一見したところ、これが何に役立つのかすぐにはわからない。Heiko Seeberger は依存するメソッドタイプの簡単な例を投稿しました。 ここで のような、依存するメソッドの型の簡単な例を投稿しましたが、そこのコメントにあるように、メソッドの型パラメータで簡単に再現することができます。というわけで、あまり説得力のある例ではありませんでした。(何か明らかなことを見逃しているかもしれません。その場合はご指摘ください)。

依存するメソッドタイプのユースケースの実用的で有用な例として、それらが代替品よりも明らかに有利であるものは何ですか?

以前は不可能であった、あるいは容易であった、どのような興味深いことができるのでしょうか?

既存の型システムの機能に対して、何が得られるのか?

また、依存メソッド型はHaskellやOCamlのような他の高度な型付け言語の型システムに見られる機能と類似していたり、そこからインスピレーションを受けているのでしょうか?

どのように解決するのですか?

メンバー型(つまりネストされた型)の使用は、多かれ少なかれ依存するメソッド型の必要性を生じさせることができます。特に、依存するメソッド型がなければ、古典的なケーキ パターンはアンチパターンに近いと私は主張します。

では、何が問題なのでしょうか?Scala のネストされた型は、それを囲むインスタンスに依存します。その結果、依存するメソッド型がない場合、そのインスタンスの外側でそれらを使おうとすると、イライラするほど難しくなります。これは、最初はエレガントで魅力的に見えたデザインを、悪夢のように硬くリファクタリングが困難な怪物に変えてしまう可能性があります。

このことを私の Scala上級トレーニングコース ,

trait ResourceManager {
  type Resource <: BasicResource
  trait BasicResource {
    def hash : String
    def duplicates(r : Resource) : Boolean
  }
  def create : Resource

  // Test methods: exercise is to move them outside ResourceManager
  def testHash(r : Resource) = assert(r.hash == "9e47088d")  
  def testDuplicates(r : Resource) = assert(r.duplicates(r))
}

trait FileManager extends ResourceManager {
  type Resource <: File
  trait File extends BasicResource {
    def local : Boolean
  }
  override def create : Resource
}

class NetworkFileManager extends FileManager {
  type Resource = RemoteFile
  class RemoteFile extends File {
    def local = false
    def hash = "9e47088d"
    def duplicates(r : Resource) = (local == r.local) && (hash == r.hash)
  }
  override def create : Resource = new RemoteFile
}

これは古典的なケーキパターンの例です。抽象化されたものがあり、それが階層化されながら徐々に洗練されていきます ( ResourceManager / ResourceFileManager / File によって絞り込まれ、さらに NetworkFileManager / RemoteFile ). これはおもちゃのような例ですが、このパターンは本物です。これはScalaコンパイラー全体で使われており、Scala Eclipseプラグインでも広範囲に使われていました。

これは抽象化の使用例です。

val nfm = new NetworkFileManager
val rf : nfm.Resource = nfm.create
nfm.testHash(rf)
nfm.testDuplicates(rf)

パス依存とは,コンパイラが testHashtestDuplicates メソッドを NetworkFileManager のメソッドは、それに対応する引数、つまり、それ自身の RemoteFiles に対応する引数で呼び出されるだけで、それ以外のものは呼び出されません。

これは紛れもなく望ましい性質ですが、このテストコードを別のソースファイルに移動したいとしたらどうでしょう?依存するメソッドタイプでは、そのメソッドを ResourceManager 階層の外側でそれらのメソッドを再定義することは些細なことです。

def testHash4(rm : ResourceManager)(r : rm.Resource) = 
  assert(r.hash == "9e47088d")

def testDuplicates4(rm : ResourceManager)(r : rm.Resource) = 
  assert(r.duplicates(r))

ここでは依存するメソッドの型を使用していることに注意してください:第2引数の型( rm.Resource ) の型は最初の引数の値 ( rm ).

依存するメソッド型なしでこれを行うことは可能ですが、非常に厄介で、メカニズムはかなり直感的ではありません。私はこのコースを2年近く教えていますが、その間、誰も即興で動く解決策を思いつかなかったのです。

自分で試してみてください ...

// Reimplement the testHash and testDuplicates methods outside
// the ResourceManager hierarchy without using dependent method types
def testHash        // TODO ... 
def testDuplicates  // TODO ...

testHash(rf)
testDuplicates(rf)

しばらく格闘していると、なぜ私が (あるいは David MacIver がそうだったのかもしれませんが、どちらがこの言葉を作ったのかは覚えていません) これを「運命のパン屋」と呼んでいるのかがわかるでしょう。

編集する。 Bakery of DoomはDavid MacIverの造語であるというのがコンセンサスである.

おまけ:Scalaの依存型全般(とその一部である依存メソッド型)の形式は、プログラミング言語である ベータ版 ... これは Beta の一貫したネストセマンティクスから自然に生まれたものです。私は、このような形で従属型を持つ、ほんの少しでも主流のプログラミング言語を他に知りません。Coq, Cayenne, Epigram, Agdaのような言語は,ある意味でより一般的な依存型付けを持っていますが,Scalaと違ってサブタイピングを持たない型システムの一部であることが大きな違いです. github リポジトリのフォークを解除するには?