[英]Scala3 "as" and "with" keywords used with "given"
Currently learning about Scala 3 implicits but I'm having a hard time grasping what the as
and with
keywords do in a definition like this:目前正在学习 Scala 3 隐含,但我很难理解
as
和with
关键字在这样的定义中的作用:
given listOrdering[A](using ord: Ordering[A]) as Ordering[List[A]] with
def compare(a: List[A], b: List[A]) = ...
I tried googeling around but didn't really find any good explanation.我试着用谷歌搜索,但并没有真正找到任何好的解释。 I've checked the Scala 3 reference guide, but the only thing I've found for
as
is that it is a "soft modifier" but that doesn't really help me understand what it does... I'm guessing that as
in the code above is somehow used for clarifying that listOrdering[A]
is an Ordering[List[A]]
(like there's some kind of typing or type casting going on?), but it would be great to find the true meaning behind it.我检查了 Scala 3 参考指南,但我发现
as
唯一一件事是它是一个“软修饰符”,但这并不能真正帮助我理解它的作用......我猜as
在上面的代码中以某种方式用于澄清listOrdering[A]
是一个Ordering[List[A]]
(就像正在进行某种类型的打字或类型转换?),但找到它背后的真正含义会很棒.
As for with
, I've only used it in Scala 2 to inherit multiple traits ( class A extends B with C with D
) but in the code above, it seems to be used in a different way...至于
with
我只在 Scala 2 中使用它来继承多个特征( class A extends B with C with D
)但在上面的代码中,它似乎以不同的方式使用......
Any explanation or pointing me in the right direction where to look documentation-wise is much appreciated!非常感谢任何解释或指出正确的方向来查看文档!
Also, how would the code above look if written in Scala 2?另外,如果用 Scala 2 编写,上面的代码会是什么样子? Maybe that would help me figure out what's going on...
也许这会帮助我弄清楚发生了什么......
The as
-keyword seems to be some artifact from earlier Dotty versions; as
-keyword 似乎是早期 Dotty 版本的一些神器; It's not used in Scala 3. The currently valid syntax would be:它没有在 Scala 3 中使用。当前有效的语法是:
given listOrdering[A](using ord: Ordering[A]): Ordering[List[A]] with
def compare(a: List[A], b: List[A]) = ???
The Scala Book gives the following rationale for the usage of with
keyword in given
-declarations: Scala 书给出了在
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
.因为在声明给定别名时,通常在等号右侧定义一个特征的匿名实例或 class,所以 Scala 提供了一种简写语法,该语法将等号和给出的别名的“新 ClassName”部分替换为
with
关键字。
ie IE
given foobar[X, Y, Z]: ClassName[X, Y, Z] = new ClassName[X, Y, Z]:
def doSomething(x: X, y: Y): Z = ???
becomes变成
given foobar[X, Y, Z]: ClassName[X, Y, Z] with
def doSomething(x: X, y: Y): Z = ???
The choice of the with
keyword seems of no particular importance: it's simply some keyword that was already reserved, and that sounded more or less natural in this context. with
关键字的选择似乎并不特别重要:它只是一些已经保留的关键字,在这种情况下听起来或多或少自然。 I guess that it's supposed to sound somewhat similar to the natural language phrases like我想它应该听起来有点类似于自然语言短语,比如
"... given a monoid structure on integers with
a • b = a * b
ande = 1
..."“...给定整数上的幺半群结构,其中
a • b = a * b
和e = 1
...”
This usage of with
is specific to given
-declarations, and does not generalize to any other contexts. with
的这种用法特定于given
的 -declarations,并且不推广到任何其他上下文。 The language reference shows that the with
-keyword appears as a terminal symbol on the right hand side of the StructuralInstance
production rule, ie this syntactic construct cannot be broken down into smaller constituent pieces that would still have the with
keyword. 语言参考表明
with
-keyword 显示为StructuralInstance
生产规则右侧的终止符号,即不能将这个句法结构分解成仍然具有with
关键字的更小的组成部分。
I believe that understanding the forces that shape the syntax is much more important than the actual syntax itself, so I'll instead describe how it arises from ordinary method definitions.我相信理解塑造语法的力量比实际的语法本身重要得多,所以我将改为描述它是如何从普通方法定义中产生的。
Foo
Foo
的实例Let's start with the assumption that we have recognized some common pattern, and named it Foo
.让我们假设我们已经识别出一些常见的模式,并将其命名为
Foo
。 Something like this:像这样的东西:
trait Foo[X]:
def bar: X
def foo(a: X, b: X): X
Foo
where we need them.Foo
的实例。 Now, assuming that we have some method f
that requires a Foo[Int]
...现在,假设我们有一些方法
f
需要Foo[Int]
...
def f[A](xs: List[A])(foo: Foo[A]): A = xs.foldLeft(foo.bar)(foo.foo)
... we could write down an instance of Foo
every time we need it: ...我们可以在每次需要时写下
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
的实例Foo
exactly where we need themFoo
的实例Writing down the methods foo
and bar
on every invocation of f
will very quickly become very boring and repetitive, so let's at least extract it into a method:在每次调用
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
}
Now we don't have to redefine foo
and bar
every time we need to invoke f
;现在我们不必每次需要调用
f
时都重新定义foo
和bar
; Instead, we can simply invoke listFoo
:相反,我们可以简单地调用
listFoo
:
f(List(List(1, 2), List(3, 4)))(listFoo[Int])
Foo
repeatedlyFoo
的实现using
using
In situations where there is basically just one canonical Foo[A]
for every A
, passing arguments such as listFoo[Int]
explicitly quickly becomes tiresome too, so instead, we declare listFoo
to be a given
, and make the foo
-parameter of f
implicit by adding using
:在每个
A
基本上只有一个规范Foo[A]
的情况下,显式传递listFoo[Int]
也很快变得令人厌烦,因此,我们将listFoo
声明为given
的,并将f
的foo
参数设为通过添加using
隐式:
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
}
Now we don't have to invoke listFoo
every time we call f
, because instances of Foo
are generated automatically:现在我们不必每次调用
f
时都调用listFoo
,因为Foo
的实例是自动生成的:
f(List(List(1, 2), List(3, 4)))
The given listFoo[A]: Foo[List[A]] = new Foo[List[A]] {
looks kinda silly, because we have to specify the Foo[List[A]]
-part twice. given listFoo[A]: Foo[List[A]] = new Foo[List[A]] {
看起来有点傻,因为我们必须指定Foo[List[A]]
-part 两次。 Instead, we can use with
:相反,我们可以使用
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
Now, there is at least no duplication in the type.现在,类型中至少没有重复。
given xyz: SomeTrait = new SomeTrait { }
is noisy, and contains duplicated partsgiven xyz: SomeTrait = new SomeTrait { }
是嘈杂的,并且包含重复的部分with
-syntax, avoid duplicationwith
-syntax,避免重复Since listFoo
is invoked by the compiler automatically, we don't really need the name, because we never use it anyway.因为
listFoo
是由编译器自动调用的,所以我们并不需要这个名字,因为我们从不使用它。 The compiler can generate some synthetic name itself:编译器可以自己生成一些合成名称:
given [A]: Foo[List[A]] with
def foo(a: List[A], b: List[A]): List[A] = a ++ b
def bar: List[A] = Nil
given
s where they aren't needed.given
s 的名称。In the end of the process, our example is transformed into something like在这个过程的最后,我们的例子变成了类似的东西
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)))
foo
/ bar
methods for List
s. List
没有重复定义foo
/ bar
方法。given
s explicitly, the compiler does this for us.given
的 s,编译器会为我们执行此操作。given
definition given
定义中没有重复的类型
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.