Scala3 "as" 和 "with" 关键字与 "given" 一起使用
Scala3 "as" and "with" keywords used with "given"
目前正在学习 Scala 3 隐式,但我很难理解 as
和 with
关键字在如下定义中的作用:
given listOrdering[A](using ord: Ordering[A]) as Ordering[List[A]] with
def compare(a: List[A], b: List[A]) = ...
我试着用谷歌搜索,但没有真正找到任何好的解释。我已经查看了 Scala 3 参考指南,但我唯一发现 as
的是它是一个“软修饰符”,但这并不能真正帮助我理解它的作用......我'我猜测上面代码中的 as
以某种方式用于澄清 listOrdering[A]
是一个 Ordering[List[A]]
(就像正在进行某种类型的打字或类型转换?),但这会很棒去寻找它背后的真正含义。
至于with
,我只在Scala 2中使用它来继承多个特征(class A extends B with C with D
),但在上面的代码中,它似乎以不同的方式使用.. .
非常感谢任何解释或指出我在何处查看文档的正确方向!
此外,如果上面的代码是用 Scala 2 编写的,会是什么样子?也许这会帮助我弄清楚发生了什么......
as
-关键字似乎是早期 Dotty 版本的产物;它未在 Scala 3 中使用。当前有效的语法为:
given listOrdering[A](using ord: Ordering[A]): Ordering[List[A]] with
def compare(a: List[A], b: List[A]) =
Scala Book 给出了在 given
声明中使用 with
关键字的以下基本原理:
Because it is common to define an anonymous instance of a trait or class to the right of the equals sign when declaring an alias given, Scala offers a shorthand syntax that replaces the equals sign and the "new ClassName" portion of the alias given with just the keyword with
.
即
given foobar[X, Y, Z]: ClassName[X, Y, Z] = new ClassName[X, Y, Z]:
def doSomething(x: X, y: Y): Z = ???
变成
given foobar[X, Y, Z]: ClassName[X, Y, Z] with
def doSomething(x: X, y: Y): Z = ???
with
关键字的选择似乎并不特别重要:它只是一些已经保留的关键字,在这种情况下听起来或多或少很自然。我猜它听起来应该与
这样的自然语言短语有些相似
"... given a monoid structure on integers with a • b = a * b
and e = 1
..."
with
的这种用法特定于 given
声明,并不推广到任何其他上下文。 language reference 表明 with
关键字作为终结符号出现在 StructuralInstance
产生式规则的右侧,即该句法结构不能分解为更小的组成部分仍然有 with
关键字。
我相信理解形成语法的力量比实际语法本身重要得多,所以我将改为描述它是如何从普通方法定义中产生的。
第 0 步:假设我们需要一些类型类的实例 Foo
让我们假设我们已经识别出一些常见的模式,并将其命名为 Foo
。像这样:
trait Foo[X]:
def bar: X
def foo(a: X, b: X): X
第 1 步:在我们需要的地方创建 Foo
的实例。
现在,假设我们有一些方法 f
需要 Foo[Int]
...
def f[A](xs: List[A])(foo: Foo[A]): A = xs.foldLeft(foo.bar)(foo.foo)
...我们可以在每次需要时写下 Foo
的实例:
f(List(List(1, 2), List(3, 4)))(new Foo[List[Int]] {
def foo(a: List[Int], b: List[Int]) = a ++ b
def bar: List[Int] = Nil
})
- 作用力:需要实例
Foo
- 解决方案:在我们需要的地方准确定义
Foo
的实例
第 2 步:方法
在每次调用 f
时写下方法 foo
和 bar
很快就会变得非常无聊和重复,所以让我们至少将其提取到一个方法中:
def listFoo[A]: Foo[List[A]] = new Foo[List[A]] {
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
}
现在我们不必每次需要调用f
时都重新定义foo
和bar
;相反,我们可以简单地调用 listFoo
:
f(List(List(1, 2), List(3, 4)))(listFoo[Int])
- 执行力:我们不想重复
Foo
的实现
- 解决方案:将实现提取到辅助方法中
第 3 步:using
在每个 A
基本上只有一个规范 Foo[A]
的情况下,显式传递诸如 listFoo[Int]
之类的参数也很快变得令人厌烦,因此我们声明 listFoo
成为 given
,并通过添加 using
:
隐含 f
的 foo
参数
def f[A](xs: List[A])(using foo: Foo[A]): A = xs.foldLeft(foo.bar)(foo.foo)
given listFoo[A]: Foo[List[A]] = new Foo[List[A]] {
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
}
现在我们不必在每次调用 f
时都调用 listFoo
,因为 Foo
的实例是自动生成的:
f(List(List(1, 2), List(3, 4)))
- 作用力:重复提供明显的规范论据令人厌烦
- 解决方案:将它们设为隐式,让编译器自动找到正确的实例
第 4 步:删除重复的类型声明
given listFoo[A]: Foo[List[A]] = new Foo[List[A]] {
看起来有点傻,因为我们必须指定 Foo[List[A]]
部分两次。相反,我们可以使用 with
:
given listFoo[A]: Foo[List[A]] with
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
现在,至少类型没有重复。
- 作用力:语法
given xyz: SomeTrait = new SomeTrait { }
嘈杂,包含重复部分
- 解决方案:使用
with
-语法,避免重复
第 5 步:不相关的名称
因为 listFoo
是由编译器自动调用的,我们真的不需要这个名字,因为我们从来没有用过它。编译器可以自己生成一些合成名称:
given [A]: Foo[List[A]] with
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
- 作用力:指定不被人类使用的不相关名称很烦人
- 解决方案:省略不需要的
given
的名称。
一起
在这个过程的最后,我们的例子变成了这样的东西
trait Foo[X]:
def foo(a: X, b: X): X
def bar: X
def f[A](xs: List[A])(using foo: Foo[A]): A = xs.foldLeft(foo.bar)(foo.foo)
given [A]: Foo[List[A]] with
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
f(List(List(1, 2), List(3, 4)))
List
没有重复定义 foo
/bar
方法。
- 无需显式传递
given
,编译器会为我们完成。
given
定义中没有重复的类型
- 没有必要为不适合人类的方法发明不相关的名称。
目前正在学习 Scala 3 隐式,但我很难理解 as
和 with
关键字在如下定义中的作用:
given listOrdering[A](using ord: Ordering[A]) as Ordering[List[A]] with
def compare(a: List[A], b: List[A]) = ...
我试着用谷歌搜索,但没有真正找到任何好的解释。我已经查看了 Scala 3 参考指南,但我唯一发现 as
的是它是一个“软修饰符”,但这并不能真正帮助我理解它的作用......我'我猜测上面代码中的 as
以某种方式用于澄清 listOrdering[A]
是一个 Ordering[List[A]]
(就像正在进行某种类型的打字或类型转换?),但这会很棒去寻找它背后的真正含义。
至于with
,我只在Scala 2中使用它来继承多个特征(class A extends B with C with D
),但在上面的代码中,它似乎以不同的方式使用.. .
非常感谢任何解释或指出我在何处查看文档的正确方向!
此外,如果上面的代码是用 Scala 2 编写的,会是什么样子?也许这会帮助我弄清楚发生了什么......
as
-关键字似乎是早期 Dotty 版本的产物;它未在 Scala 3 中使用。当前有效的语法为:
given listOrdering[A](using ord: Ordering[A]): Ordering[List[A]] with
def compare(a: List[A], b: List[A]) =
Scala Book 给出了在 given
声明中使用 with
关键字的以下基本原理:
Because it is common to define an anonymous instance of a trait or class to the right of the equals sign when declaring an alias given, Scala offers a shorthand syntax that replaces the equals sign and the "new ClassName" portion of the alias given with just the keyword
with
.
即
given foobar[X, Y, Z]: ClassName[X, Y, Z] = new ClassName[X, Y, Z]:
def doSomething(x: X, y: Y): Z = ???
变成
given foobar[X, Y, Z]: ClassName[X, Y, Z] with
def doSomething(x: X, y: Y): Z = ???
with
关键字的选择似乎并不特别重要:它只是一些已经保留的关键字,在这种情况下听起来或多或少很自然。我猜它听起来应该与
"... given a monoid structure on integers with
a • b = a * b
ande = 1
..."
with
的这种用法特定于 given
声明,并不推广到任何其他上下文。 language reference 表明 with
关键字作为终结符号出现在 StructuralInstance
产生式规则的右侧,即该句法结构不能分解为更小的组成部分仍然有 with
关键字。
我相信理解形成语法的力量比实际语法本身重要得多,所以我将改为描述它是如何从普通方法定义中产生的。
第 0 步:假设我们需要一些类型类的实例 Foo
让我们假设我们已经识别出一些常见的模式,并将其命名为 Foo
。像这样:
trait Foo[X]:
def bar: X
def foo(a: X, b: X): X
第 1 步:在我们需要的地方创建 Foo
的实例。
现在,假设我们有一些方法 f
需要 Foo[Int]
...
def f[A](xs: List[A])(foo: Foo[A]): A = xs.foldLeft(foo.bar)(foo.foo)
...我们可以在每次需要时写下 Foo
的实例:
f(List(List(1, 2), List(3, 4)))(new Foo[List[Int]] {
def foo(a: List[Int], b: List[Int]) = a ++ b
def bar: List[Int] = Nil
})
- 作用力:需要实例
Foo
- 解决方案:在我们需要的地方准确定义
Foo
的实例
第 2 步:方法
在每次调用 f
时写下方法 foo
和 bar
很快就会变得非常无聊和重复,所以让我们至少将其提取到一个方法中:
def listFoo[A]: Foo[List[A]] = new Foo[List[A]] {
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
}
现在我们不必每次需要调用f
时都重新定义foo
和bar
;相反,我们可以简单地调用 listFoo
:
f(List(List(1, 2), List(3, 4)))(listFoo[Int])
- 执行力:我们不想重复
Foo
的实现 - 解决方案:将实现提取到辅助方法中
第 3 步:using
在每个 A
基本上只有一个规范 Foo[A]
的情况下,显式传递诸如 listFoo[Int]
之类的参数也很快变得令人厌烦,因此我们声明 listFoo
成为 given
,并通过添加 using
:
f
的 foo
参数
def f[A](xs: List[A])(using foo: Foo[A]): A = xs.foldLeft(foo.bar)(foo.foo)
given listFoo[A]: Foo[List[A]] = new Foo[List[A]] {
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
}
现在我们不必在每次调用 f
时都调用 listFoo
,因为 Foo
的实例是自动生成的:
f(List(List(1, 2), List(3, 4)))
- 作用力:重复提供明显的规范论据令人厌烦
- 解决方案:将它们设为隐式,让编译器自动找到正确的实例
第 4 步:删除重复的类型声明
given listFoo[A]: Foo[List[A]] = new Foo[List[A]] {
看起来有点傻,因为我们必须指定 Foo[List[A]]
部分两次。相反,我们可以使用 with
:
given listFoo[A]: Foo[List[A]] with
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
现在,至少类型没有重复。
- 作用力:语法
given xyz: SomeTrait = new SomeTrait { }
嘈杂,包含重复部分 - 解决方案:使用
with
-语法,避免重复
第 5 步:不相关的名称
因为 listFoo
是由编译器自动调用的,我们真的不需要这个名字,因为我们从来没有用过它。编译器可以自己生成一些合成名称:
given [A]: Foo[List[A]] with
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
- 作用力:指定不被人类使用的不相关名称很烦人
- 解决方案:省略不需要的
given
的名称。
一起
在这个过程的最后,我们的例子变成了这样的东西
trait Foo[X]:
def foo(a: X, b: X): X
def bar: X
def f[A](xs: List[A])(using foo: Foo[A]): A = xs.foldLeft(foo.bar)(foo.foo)
given [A]: Foo[List[A]] with
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
f(List(List(1, 2), List(3, 4)))
List
没有重复定义foo
/bar
方法。- 无需显式传递
given
,编译器会为我们完成。 given
定义中没有重复的类型- 没有必要为不适合人类的方法发明不相关的名称。