在 Scala 中使用种类投影仪部分应用类型 lambda
Partially applied type lambda in Scala with kind projector
考虑以下类型定义:
trait LiftF[F[_], G[_]] {
def liftF[A](fa: F[A]): G[A]
}
当在上下文边界(使用 kind projector 插件)中提供这种类型的隐式要求时,我们必须这样写:
def func[A, G[_], F[_]: LiftF[?[_], G]](a: F[A]): G[A]
我想去掉 ?[_]
部分,所以我最初的猜测是写一个 To[G[_]]
类型 returns LiftF[?[_], G]
来转换上面的函数定义成
def func[A, G[_], F[_]: LiftF.To[G]](a: F[A]): G[A]
然而,当写类型 To
定义为
type To[G[_]] = LiftF[?[_], G]
我得到以下编译错误:
Error:(17, 20) type Λ$ takes type parameters
type To[G[_]] = LiftF[?[_], G]
尝试用现有类型重写它会产生以下类型定义:
type To[G[_]] = LiftF[F, G] forSome { type F[X] }
这编译得很好,但不出所料,不能应用于其他类型参数,因此无法实现所需的函数定义。
我设法使用受 aux 模式启发的代码实现了 "partial application" 部分:
trait To[G[_]] {
type From[F[_]] = LiftF[F, G]
}
可悲的是,这给我留下了比原来的语法更糟糕的语法:
def func[A, G[_], F[_]: LiftF.To[G]#From](a: F[A]): G[A]
我的问题是 - 我能否在 kind projector 的帮助下在 Scala 中实现最初提出的语法,或者我应该坚持使用 ?[_]
?
据我了解,那种投影仪在这里帮不了你:
type To[G[_]] = LiftF[?[_], G]
会简单地被机械地重写成类似
的东西
type To[G[_]] = ({ type T[F[_]] = LiftF[F, G] })#T
但它在 2.12.x 中是无效的,因为它期望在定义的右侧有一个普通类型 *
。
如果将参数 F
移到左侧,最终会得到
type To[G[_], F[_]] = LiftF[F, G]
然后您必须将其用作 To[G, ?[_]]
,这显然也不会给您带来任何好处,它只是交换参数的顺序。因此,我建议只使用 LiftF[?[_], G]
并从不必明确写出 ({ type L[F[_]] = LiftF[F, G] })#L
的事实中得到安慰。
顺便说一下,在 Dotty 中,这工作得很好:
trait LiftF[F[_], G[_]] {
def liftF[A](fa: F[A]): G[A]
}
type To[G[_]] = [F[_]] => LiftF[F, G]
def f[A, G[_], F[_]: To[G]](a: F[A]): G[A] = implicitly[LiftF[F, G]].liftF(a)
考虑以下类型定义:
trait LiftF[F[_], G[_]] {
def liftF[A](fa: F[A]): G[A]
}
当在上下文边界(使用 kind projector 插件)中提供这种类型的隐式要求时,我们必须这样写:
def func[A, G[_], F[_]: LiftF[?[_], G]](a: F[A]): G[A]
我想去掉 ?[_]
部分,所以我最初的猜测是写一个 To[G[_]]
类型 returns LiftF[?[_], G]
来转换上面的函数定义成
def func[A, G[_], F[_]: LiftF.To[G]](a: F[A]): G[A]
然而,当写类型 To
定义为
type To[G[_]] = LiftF[?[_], G]
我得到以下编译错误:
Error:(17, 20) type Λ$ takes type parameters
type To[G[_]] = LiftF[?[_], G]
尝试用现有类型重写它会产生以下类型定义:
type To[G[_]] = LiftF[F, G] forSome { type F[X] }
这编译得很好,但不出所料,不能应用于其他类型参数,因此无法实现所需的函数定义。
我设法使用受 aux 模式启发的代码实现了 "partial application" 部分:
trait To[G[_]] {
type From[F[_]] = LiftF[F, G]
}
可悲的是,这给我留下了比原来的语法更糟糕的语法:
def func[A, G[_], F[_]: LiftF.To[G]#From](a: F[A]): G[A]
我的问题是 - 我能否在 kind projector 的帮助下在 Scala 中实现最初提出的语法,或者我应该坚持使用 ?[_]
?
据我了解,那种投影仪在这里帮不了你:
type To[G[_]] = LiftF[?[_], G]
会简单地被机械地重写成类似
的东西type To[G[_]] = ({ type T[F[_]] = LiftF[F, G] })#T
但它在 2.12.x 中是无效的,因为它期望在定义的右侧有一个普通类型 *
。
如果将参数 F
移到左侧,最终会得到
type To[G[_], F[_]] = LiftF[F, G]
然后您必须将其用作 To[G, ?[_]]
,这显然也不会给您带来任何好处,它只是交换参数的顺序。因此,我建议只使用 LiftF[?[_], G]
并从不必明确写出 ({ type L[F[_]] = LiftF[F, G] })#L
的事实中得到安慰。
顺便说一下,在 Dotty 中,这工作得很好:
trait LiftF[F[_], G[_]] {
def liftF[A](fa: F[A]): G[A]
}
type To[G[_]] = [F[_]] => LiftF[F, G]
def f[A, G[_], F[_]: To[G]](a: F[A]): G[A] = implicitly[LiftF[F, G]].liftF(a)