当说scala通过对象语法提供一流的模块支持时,它是什么意思?词汇表中没有任何内容甚至提到这句话,但我现在已经遇到过两次并且无法破译它。在this博客文章中有关于适配器的说法。
答案 0 :(得分:13)
A"模块"是一个可插拔的软件,也称为"软件包"有时。它通过一组定义明确的接口提供功能,这些接口声明了它提供的内容和需要的内容。最后,它是可以互换的。
很少有语言直接支持模块,主要是因为虽然支持声明API很常见,但声明依赖关系或要求的支持却不常见。流行语言的库通常依赖于"标准"提供的类型。库,或要求使用实现它们提供的API的对象进行初始化。
因此,如果我想制作一个基准模块,我通常会使用标准库提供的时钟设备,或者更糟糕的是,我会声明一个时钟类型并请求初始化在模块的功能可以使用之前实现它的类。
然而,当模块支持可用时,我不仅要声明我提供的基准接口,而且还要声明我需要一个"时钟模块" - 导出我需要的某些接口的模块。
我的模块的客户端不会被要求做任何事情来使用我的接口 - 它可以继续使用它。或者它甚至无法声明将使用我的基准模块,而是声明它需要基准模块。
只有在" top"才能满足要求。级别,在应用程序级别(模块是应用程序的组件)。此时它将声明它将使用该客户端,我的基准测试和实现我的时钟要求的模块。
如果你认识Guice,这似乎很熟悉。 Guice解决的问题在很大程度上是由于Java编程语言缺乏模块支持造成的。
所以,回到Scala。模块支持如何工作?好吧,我模块的界面可能如下所示:
trait Benchmark extends Clock // What I need {
// What I provide
type ABench <: Bench
trait Bench {
def measure(task: => Unit): Long
}
def aBench: ABench
}
和Clock将是一个模块定义,例如:
trait Clock {
// What it provides
type AClock <: Clock
trait Clock {
def now(): Long
}
def aClock: AClock
}
我的模块本身可能如下所示:
trait MyModule extends Benchmark {
class ABench extends Bench {
def measure(task: => Unit): Long = {
val measurements = for(_ <- 1 to 10) yield {
val start = aClock.now()
task
val end = aClock.now()
end - start
}
measurements / 10
}
}
object aBench extends ABench
}
时钟模块将被类似地定义。应用程序可以声明为模块的组合:
trait application extends Clock with Benchmark with ...
但是,当然,不需要声明依赖关系,因为已经提供了依赖关系。然后,您可以组合提供构建应用程序要求的模块:
object Application extends MyModule with JavaClock with ...
这会将MyModule的要求与JavaClock提供的实现联系起来。上面的内容仍然需要一些共享的知识,因为&#34;时钟&#34;可能是 I 提供的API。当然,人们可以编写代理,但它不是即插即用的。如果我宣布我的Benchmark模块是这样的话,那么Scala可以更进一步:
trait Benchmark {
type Clock = {
def now(): Long
}
def aClock: Clock
// What I provide
type ABench <: Bench
trait Bench {
def measure(task: => Unit): Long
}
def aBench: ABench
}
现在任何提供now():Long方法的类都可以用来满足需求,而不需要任何桥接。当然,如果方法的名称是&#34; millis():Long&#34;而不是&#34; now():Long&#34;,我仍然搞砸了,那种&#34;绑定&#34;语言提供模块支持可能会解决,但不是Scala。此外,由于JVM的工作原理,还存在性能损失。
那么,那就是模块和模块支持。最后第一类模块。对X的第一类支持意味着X可以作为值进行操作。例如,Scala具有对函数的第一类支持,这意味着我可以将函数传递给方法,将其存储在变量中,映射中等等。
对模块的第一类支持基本上是实例化,尽管可以使用&#34;对象&#34;创建该模块的单例,然后传递它(我在下面进一步讨论的优点)。所以,我可以这样做:
object myBenchmark extends MyModule with JVMClock
并将myBenchmark作为参数传递给需要此类模块的方法。
Scala中有两个元素可以完成所有这些工作:抽象类型和路径相关类型。
抽象类型,&#34;类型&#34;声明,使一段代码可以声明它将使用类型X, not 将由谁调用或实例化它来定义,但是,相反,目前模块得到组成
路径依赖类型使得可以使用模块而不会完全不安全,但没有限制性而不允许任何事情。让我们说我这样做:
val b: MyModule.Clock = MyModule.aClock
让我们说我在Benchmark上有一个以Clock作为参数的方法。我可以在MyModule上调用该方法,将b作为参数传递,因为Scala知道b的Clock是绑定到MyModule的时钟。如果我尝试将b传递给实现Benchmark的另一个模块,Scala不会让我这样做。也就是说,我可以从Benchmark中获取特定于抽象类型的Benchmark的值 - 除了模块实现以外都不知道 - 并将其反馈给Benchmark而不是其他Benchmark实现。