此文档页面特定于 Scala 3,并且可能涵盖 Scala 2 中不可用的新概念。除非另有说明,否则此页面中的所有代码示例均假定你使用的是 Scala 3。
依赖函数类型描述函数类型,其中结果类型可能取决于函数的参数值。依赖类型和依赖函数类型是一个更高级的概念,你通常只有在设计自己的库或使用高级库时才会遇到它。
依赖方法类型
让我们考虑以下异构数据库的示例,该数据库可以存储不同类型的值。键包含有关相应值类型的信息
trait Key { type Value }
trait DB {
def get(k: Key): Option[k.Value] // a dependent method
}
给定一个键,方法 get
让我们访问映射,并可能返回类型为 k.Value
的存储值。我们可以将此路径依赖类型解读为:“根据参数 k
的具体类型,我们返回一个匹配的值”。
例如,我们可以有以下键
object Name extends Key { type Value = String }
object Age extends Key { type Value = Int }
现在,对方法 get
的以下调用将进行类型检查
val db: DB = ...
val res1: Option[String] = db.get(Name)
val res2: Option[Int] = db.get(Age)
调用方法 db.get(Name)
会返回类型为 Option[String]
的值,而调用 db.get(Age)
会返回类型为 Option[Int]
的值。返回类型取决于传递给 get
的参数的具体类型——因此得名依赖类型。
依赖函数类型
如上所述,Scala 2 已经支持依赖方法类型。但是,创建类型为 DB
的值非常麻烦
// a user of a DB
def user(db: DB): Unit =
db.get(Name) ... db.get(Age)
// creating an instance of the DB and passing it to `user`
user(new DB {
def get(k: Key): Option[k.Value] = ... // implementation of DB
})
我们需要手动创建一个 DB
的匿名内部类,实现 get
方法。对于依赖于创建 DB
的许多不同实例的代码来说,这是非常繁琐的。
特质 DB
只有一个抽象方法 get
。如果我们可以改用 lambda 语法,那不是很好吗?
user { k =>
... // implementation of DB
}
事实上,这现在在 Scala 3 中是可能的!我们可以将 DB
定义为依赖函数类型
type DB = (k: Key) => Option[k.Value]
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// A dependent function type
给定这个 DB
的定义,对 user
的上述调用会按原样进行类型检查。
您可以在参考文档中阅读有关依赖函数类型的内部机制的更多信息。
案例研究:数值表达式
让我们假设我们要定义一个对数字的内部表示进行抽象的模块。例如,这对于实现自动派生的库很有用。
我们从定义数字模块开始
trait Nums:
// the type of numbers is left abstract
type Num
// some operations on numbers
def lit(d: Double): Num
def add(l: Num, r: Num): Num
def mul(l: Num, r: Num): Num
我们省略了
Nums
的具体实现,但作为练习,您可以通过将type Num = Double
分配给Nums
并相应地实现方法来实现Nums
。
现在使用我们的数字抽象的程序具有以下类型
type Prog = (n: Nums) => n.Num => n.Num
val ex: Prog = nums => x => nums.add(nums.lit(0.8), x)
计算 ex
等程序的导数的函数的类型是
def derivative(input: Prog): Double
借助依赖函数类型的便利性,使用不同的程序调用此函数非常方便
derivative { nums => x => x }
derivative { nums => x => nums.add(nums.lit(0.8), x) }
// ...
回想一下,上述编码中的相同程序将是
derivative(new Prog {
def apply(nums: Nums)(x: nums.Num): nums.Num = x
})
derivative(new Prog {
def apply(nums: Nums)(x: nums.Num): nums.Num = nums.add(nums.lit(0.8), x)
})
// ...
与上下文函数结合
扩展方法、上下文函数和依赖函数的组合为库设计人员提供了一个强大的工具。例如,我们可以按如下方式改进我们上面的库
trait NumsDSL extends Nums:
extension (x: Num)
def +(y: Num) = add(x, y)
def *(y: Num) = mul(x, y)
def const(d: Double)(using n: Nums): n.Num = n.lit(d)
type Prog = (n: NumsDSL) ?=> n.Num => n.Num
// ^^^
// prog is now a context function that implicitly
// assumes a NumsDSL in the calling context
def derivative(input: Prog): Double = ...
// notice how we do not need to mention Nums in the examples below?
derivative { x => const(1.0) + x }
derivative { x => x * x + const(2.0) }
// ...
此页面贡献者
内容
- 简介
- Scala 特性
- 为何选择 Scala 3?
- Scala 入门
- Hello, World!
- REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 一等函数
- 单例对象
- 集合
- 上下文抽象
- 顶层定义
- 总结
- 类型初探
- 字符串插值
- 控制结构
- 领域建模
- 工具
- 面向对象建模
- 函数式建模
- 方法
- 方法特性
- Scala 3 中的主方法
- 总结
- 函数
- 匿名函数
- 函数变量
- Eta 扩展
- 高阶函数
- 编写自己的 map 方法
- 创建返回函数的方法
- 总结
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 总结
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数即值
- 函数式错误处理
- 总结
- 类型和类型系统
- 推断类型
- 泛型
- 交集类型
- 并集类型
- 代数数据类型
- 变异
- 不透明类型
- 结构类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- 上下文参数
- 上下文界限
- 给定导入
- 类型类
- 多重相等
- 隐式转换
- 总结
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- 工作表
- 与 Java 交互
- 面向 Java 开发人员的 Scala
- 面向 JavaScript 开发人员的 Scala
- 面向 Python 开发人员的 Scala
- 下一步