目前了解斯卡拉3個implicits,但我有一個很難把握的東西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 撰寫,上面的代碼會怎樣?也許這會幫助我弄清楚發生了什么......
uj5u.com熱心網友回復:
該as-keyword似乎是從早期版本的斑點狗一些假象; 它沒有在 Scala 3 中使用。當前有效的語法是:
given listOrdering[A](using ord: Ordering[A]): Ordering[List[A]] with
?def compare(a: List[A], b: List[A]) =
在斯卡拉書給出的使用下列理由with關鍵字given-declarations:
因為在宣告給定的別名時,通常在等號右側定義一個 trait 或類的匿名實體,所以 Scala 提供了一種速記語法來替換等號和給定別名的“new ClassName”部分。關鍵字
with。
IE
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關鍵字的選擇似乎并不特別重要:它只是一些已經被保留的關鍵字,在這種情況下聽起來或多或少自然。我想它應該聽起來有點類似于自然語言短語,例如
“......給定一個帶有
a ? b = a * b和的整數的幺半群結構e = 1......”
的這種用法with特定于given-declarations,不能推廣到任何其他背景關系。的語言參考示出了with-keyword顯示為在右手側上的終端符號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 步:方法
寫下方法foo和bar每次呼叫fwill 很快就會變得非常無聊和重復,所以讓我們至少將它提取到一個方法中:
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
}
現在我們不必重新定義foo,bar每次我們都需要呼叫f;相反,我們可以簡單地呼叫listFoo:
f(List(List(1, 2), List(3, 4)))(listFoo[Int])
- 執行力:我們不想
Foo重復寫下的實作 - 解決方案:將實作提取到一個輔助方法中
第 3 步: using
在情況下有基本上只是一個規范的Foo[A]每一個A,傳遞引數如listFoo[Int]明確很快變得無聊了,所以反而,我們宣告listFoo是一個given,使foo的-parameterf加入隱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(List(List(1, 2), List(3, 4)))
- Acting force: Repeatedly supplying obvious canonical arguments is tiresome
- Solution: make them implicit, let the compiler find the right instances automatically
Step 4: Deduplicate type declarations
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. Instead, we can use 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 - Solution: Use
with-syntax, avoid duplication
Step 5: irrelevant names
由于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不需要的s的名稱。
全部一起
在這個程序的最后,我們的例子被轉換成類似
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)))
- s的
foo/bar方法沒有重復定義List。 - 不需要
given顯式傳遞s,編譯器會為我們做這件事。 given定義中沒有重復的型別- 沒有必要為不適合人類使用的方法發明不相關的名稱。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/377878.html
