scala3 扩展方法类型参数

scala3 extension method type parameter

这是我的 scala2 代码到 scala3 的直接翻译

trait Narrow[F[_], A, B <: A: ClassTag]:
  def apply(fa: F[A]): F[B]

extension [F[_], A] (fa: F[A]):
  def narrow[B: ClassTag] (using op: Narrow[F, A, B]): F[B] = op(fa)

我需要在调用站点指定窄操作的类型,但是扩展方法不允许该语法。针对此限制有哪些最佳解决方法?

这样做的目的是能够缩小集合/尝试/其他任何类型中的类型。窄类型 class 将平面映射内部的任何内容,比较运行时类型,如果匹配则将 B 包装在 F 中,否则 return 空 F

trait A
trait B extends A
object A extends A
object B extends B

val bb: List[B] = List(A, A, B, B, A)
  .narrow[B]
assert(bb == List(B, B))

你可以使用多态函数,如果你能处理丑陋的话:

extension [F[_], A] (fa: F[A]):
  def narrow() = [B <: A] => (using op: Narrow[F, A, B]) => op(fa)

然后您可以使用 foo.narrow()[String] 调用它。 Here 在斯卡斯蒂。

narrow() 是必要的,因为没有它,类型参数将转到扩展而不是多态函数。

将来,Scala 3 可能允许类型参数直接传递给扩展内的方法,但现在,您可以继续使用 Scala 2 隐式 class 并在下一个版本后更改它:

implicit class NarrowOps[F[_], A](fa: F[A]):
  def narrow[B <: A](using op: Narrow[F, A, B]) = op(fa)

Scastie

旁注:您不需要在扩展中再次使用 B: ClassTag,尽管我相信您确实需要使用绑定 B <: A.

我无法忍受呼叫站点上的 ()。我决定尝试使用仅带有类型参数的 apply 方法隐式转换为类型。

trait NarrowTypeClass[F[_], A, B <: A: ClassTag]:
  def apply(fa: F[A]): F[B]

given [F[_], A] as Conversion[F[A], Narrowable[F, A]] = Narrowable(_)

sealed class Narrowable [F[_], A] (fa: F[A]):
  def narrow[B <: A: ClassTag] (using op: NarrowTypeClass[F, A, B]): F[B] = op(fa)

这似乎可以解决问题