Scala中的部分应用的lambda类型与投影仪

you*_*tur 6 types scala partial-application higher-kinded-types kind-projector

考虑以下类型定义:

trait LiftF[F[_], G[_]] {
  def liftF[A](fa: F[A]): G[A]
}
Run Code Online (Sandbox Code Playgroud)

在上下文边界中提供对此类隐式的要求时(使用种类投影仪插件),我们必须这样编写:

def func[A, G[_], F[_]: LiftF[?[_], G]](a: F[A]): G[A]
Run Code Online (Sandbox Code Playgroud)

我想摆脱这一?[_]部分,所以我最初的猜测是编写一个To[G[_]]返回的类型,LiftF[?[_], G]以将上述函数定义转换为

def func[A, G[_], F[_]: LiftF.To[G]](a: F[A]): G[A]
Run Code Online (Sandbox Code Playgroud)

但是,在将类型To定义写为

type To[G[_]] = LiftF[?[_], G]
Run Code Online (Sandbox Code Playgroud)

我收到以下编译错误:

Error:(17, 20) type ?$ takes type parameters
type To[G[_]] = LiftF[?[_], G]
Run Code Online (Sandbox Code Playgroud)

尝试使用存在性类型重写它会产生以下类型定义:

type To[G[_]] = LiftF[F, G] forSome { type F[X] }
Run Code Online (Sandbox Code Playgroud)

这可以很好地编译,但是,毫无疑问,它不能应用于其他类型参数,因此无法实现所需的函数定义。

我设法通过受aux模式启发的代码实现了“部分应用程序”部分:

trait To[G[_]] {
  type From[F[_]] = LiftF[F, G]
}
Run Code Online (Sandbox Code Playgroud)

可悲的是,这给我留下了比原始语法差的语法:

def func[A, G[_], F[_]: LiftF.To[G]#From](a: F[A]): G[A]
Run Code Online (Sandbox Code Playgroud)

我的问题是-我可以借助同类投影仪来实现Scala中最初提出的语法,还是应该坚持使用?[_]

And*_*kin 6

据我了解,实物投影仪在这里不能真正帮助您:

type To[G[_]] = LiftF[?[_], G]
Run Code Online (Sandbox Code Playgroud)

只会被机械地重写成类似

type To[G[_]] = ({ type T[F[_]] = LiftF[F, G] })#T
Run Code Online (Sandbox Code Playgroud)

但在2.12.x中无效,因为它*在定义的右侧需要一个普通类型的种类。

如果将参数F移到左侧,最终会得到

type To[G[_], F[_]] = LiftF[F, G]
Run Code Online (Sandbox Code Playgroud)

然后您必须将其用作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)
Run Code Online (Sandbox Code Playgroud)