简体   繁体   English

Scala3“as”和“with”关键字与“given”一起使用

[英]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 隐含,但我很难理解aswith关键字在这样的定义中的作用:

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 and e = 1 ..." “...给定整数上的幺半群结构,其中a • b = a * be = 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.我相信理解塑造语法的力量比实际的语法本身重要得多,所以我将改为描述它是如何从普通方法定义中产生的。

Step 0: Assume that we need instances of some typeclass Foo步骤 0:假设我们需要一些类型类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

Step 1: Create instances of Foo where we need them.第 1 步:在我们需要的地方创建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
})
  • Acting force: Need for instances of Foo作用力:需要Foo的实例
  • Solution: Defining instances of Foo exactly where we need them解决方案:在我们需要的地方准确定义Foo的实例

Step 2: Methods第 2 步:方法

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时写下方法foobar很快就会变得非常无聊和重复,所以我们至少将它提取到一个方法中:

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时都重新定义foobar Instead, we can simply invoke listFoo :相反,我们可以简单地调用listFoo

f(List(List(1, 2), List(3, 4)))(listFoo[Int])
  • Acting force: We don't want to write down implementations of Foo repeatedly作用力:我们不想重复写下Foo的实现
  • Solution: extract the implementation into a helper method解决方案:将实现提取到辅助方法中

Step 3: using第 3 步: 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的,并将ffoo参数设为通过添加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)))
  • Acting force: Repeatedly supplying obvious canonical arguments is tiresome作用力:反复供应明显的标准arguments烦
  • Solution: make them implicit, let the compiler find the right instances automatically解决方案:让它们隐式,让编译器自动找到正确的实例

Step 4: Deduplicate type declarations第 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.现在,类型中至少没有重复。

  • Acting force: The syntax given xyz: SomeTrait = new SomeTrait { } is noisy, and contains duplicated parts作用力: given xyz: SomeTrait = new SomeTrait { }是嘈杂的,并且包含重复的部分
  • Solution: Use with -syntax, avoid duplication解决方案: with -syntax,避免重复

Step 5: irrelevant names第 5 步:不相关的名称

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
  • Acting force: specifying irrelevant names that aren't used by humans anyway is tiresome作用力:指定人类无论如何都不会使用的无关名称是令人厌烦的
  • Solution: omit the name of the given s where they aren't needed.解决方案:在不需要的地方省略given s 的名称。

All together全部一起

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)))
  • There is no repetitive definition of foo / bar methods for List s. List没有重复定义foo / bar方法。
  • There is no need to pass the given s explicitly, the compiler does this for us.不需要显式传递given的 s,编译器会为我们执行此操作。
  • There is no duplicated type in the given definition given定义中没有重复的类型
  • There is no need to invent irrelevant names for methods that are not intended for humans.没有必要为不适合人类的方法发明不相关的名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM