以前は実験的な機能であった依存メソッドタイプが、トランクでデフォルトで有効になりました。これにより、Scalaコミュニティに興奮が生じたようです。
一見すると、これが何に役立つかはすぐにはわかりません。Heiko Seebergerは、依存メソッドタイプの簡単な例をここに投稿しました。コメントに示されているように、メソッドのタイプパラメータを使用して簡単に再現できます。したがって、それはあまり説得力のある例ではありませんでした。(明らかな何かが欠けている可能性があります。その場合は修正してください。)
代替メソッドよりも明らかに有利な、依存メソッドタイプのユースケースの実用的で有用な例は何ですか?
以前は不可能/簡単であった、それらを使ってどのような興味深いことができますか?
彼らは既存の型システム機能に対して何を購入しますか?
また、依存メソッドタイプは、Haskell、OCamlなどの他の高度な型付き言語の型システムに見られる機能に類似しているか、そこからインスピレーションを得ていますか?
多かれ少なかれ、メンバー(つまりネストされた)タイプを使用すると、依存するメソッドタイプが必要になる可能性があります。特に、依存するメソッドタイプがない場合、従来のケーキパターンはアンチパターンに近いと私は主張します。
だから問題は何ですか?Scalaのネストされた型は、それらを囲むインスタンスに依存しています。その結果、依存するメソッドタイプがない場合、そのインスタンスの外部でそれらを使用しようとすると、イライラするほど困難になる可能性があります。これにより、最初はエレガントで魅力的に見えるデザインが、悪夢のように硬く、リファクタリングが難しい怪物に変わる可能性があります。
AdvancedScalaトレーニングコースで行う演習でそれを説明します。
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
/Resource
で洗練されているFileManager
/File
で洗練された順番にですNetworkFileManager
/ RemoteFile
)。これはおもちゃの例ですが、パターンは本物です。Scalaコンパイラ全体で使用され、ScalaEclipseプラグインで広く使用されていました。
使用中の抽象化の例を次に示します。
val nfm = new NetworkFileManager
val rf : nfm.Resource = nfm.create
nfm.testHash(rf)
nfm.testDuplicates(rf)
注コンパイラがいることを保証することにパス依存手段ものtestHash
とtestDuplicates
上のメソッド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 ofDoomと呼んでいる理由がわかるでしょう。
編集:コンセンサスは、Bakery ofDoomがDavidMacIverの造語であったということです...
ボーナスとして:Scalaの一般的な依存型の形式(およびその一部としての依存メソッド型)は、プログラミング言語Betaに触発されました...それらはBetaの一貫したネストセマンティクスから自然に発生します。この形式の依存型を持つ、かすかに主流のプログラミング言語でさえ、他に知りません。Coq、Cayenne、Epigram、Agdaなどの言語には、いくつかの点でより一般的な依存型の異なる形式がありますが、Scalaとは異なり、サブ型がない型システムの一部であるという点で大きく異なります。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加