成员(即嵌套)类型的任何使用或多或少都会引起对依赖方法类型的需求。特别是,我认为如果没有依赖的方法类型,经典的蛋糕模式更接近于反模式。
所以有什么问题? 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
/Resource
被精炼为FileManager
/File
又经过精炼NetworkFileManager
/RemoteFile
)。这是一个玩具示例,但该模式是真实的:它在整个 Scala 编译器中使用,并在 Scala Eclipse 插件中广泛使用。
这是使用抽象的示例,
val nfm = new NetworkFileManager
val rf : nfm.Resource = nfm.create
nfm.testHash(rf)
nfm.testDuplicates(rf)
请注意,路径依赖意味着编译器将保证testHash
and 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))
请注意此处依赖方法类型的使用:第二个参数的类型 (rm.Resource
) 取决于第一个参数的值 (rm
).
在没有依赖方法类型的情况下可以做到这一点,但它非常尴尬,而且机制也很不直观:我已经教授这门课程近两年了,在那段时间里,没有人能自发地提出一个可行的解决方案。
自己尝试一下...
// 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)
经过一段时间的挣扎后,你可能会发现为什么我(或者也许是大卫·麦基弗,我们不记得我们中谁创造了这个词)将其称为“末日面包店”。
Edit:共识是《末日面包店》是大卫·麦基弗的创造物……
额外的好处是:Scala 的依赖类型形式(以及依赖方法类型作为其中的一部分)受到编程语言的启发Beta...它们自然地源于 Beta 一致的嵌套语义。我不知道有任何其他甚至微弱的主流编程语言具有这种形式的依赖类型。 Coq、Cayenne、Epigram 和 Agda 等语言具有不同形式的依赖类型,这种类型在某些方面更为通用,但由于属于类型系统的一部分而存在显着差异,与 Scala 不同,类型系统没有子类型。