什么是Scala中的类型lambda,它们有什么好处?

7 浏览
0 Comments

什么是Scala中的类型lambda,它们有什么好处?

有时我会在Scala博客文章中遇到半神秘的符号

def f[T](..) = new T[({type l[A]=SomeType[A,..]})#l] {..} 

,并且作者称其为\"我们使用了类型λ技巧\"。虽然我对此有些直觉(我们获得了一个匿名类型参数A而不必向定义中添加它?),但是我找不到清晰的来源来描述类型λ技巧以及其好处。它只是一种语法糖,还是它开启了一些新的维度?

admin 更改状态以发布 2023年5月20日
0
0 Comments

这些好处与匿名函数所带来的好处完全相同。

def inc(a: Int) = a + 1; List(1, 2, 3).map(inc)
List(1, 2, 3).map(a => a + 1)

举个例子,使用Scalaz 7。我们想要使用一个 Functor,它可以在 Tuple2 的第二个元素上映射一个函数。

type IntTuple[+A]=(Int, A)
Functor[IntTuple].map((1, 2))(a => a + 1)) // (1, 3)
Functor[({type l[a] = (Int, a)})#l].map((1, 2))(a => a + 1)) // (1, 3)

Scalaz提供一些隐式转换,可以推断出 Functor 的类型参数,因此我们经常避免编写这些转换。前一行可重写为:

(1, 2).map(a => a + 1) // (1, 3)

如果您使用的是Intellij,您可以启用 Settings,Code Style,Scala,Folding,Type Lambdas。然后,这将隐藏语法的糟糕部分,呈现更可接受的内容:

Functor[[a]=(Int, a)].map((1, 2))(a => a + 1)) // (1, 3)

Scala的未来版本可能会直接支持这样的语法。

0
0 Comments

在使用高阶类型时,类型lambda非常重要。

考虑一个简单的例子,定义Either[A, B]的右投影的monad。Monad类型类看起来像这样:

trait Monad[M[_]] {
  def point[A](a: A): M[A]
  def bind[A, B](m: M[A])(f: A => M[B]): M[B]
}

现在,Either是一个具有两个参数的类型构造器,但是为了实现Monad,您需要给它一个具有一个参数的类型构造器。解决这个问题的方法是使用类型lambda:

class EitherMonad[A] extends Monad[({type λ[α] = Either[A, α]})#λ] {
  def point[B](b: B): Either[A, B]
  def bind[B, C](m: Either[A, B])(f: B => Either[A, C]): Either[A, C]
}

这是类型系统中的柯里化示例-您已将Either的类型柯里化,因此当您要创建EitherMonad的实例时,您必须指定其中一个类型;当然,其他的在调用point或bind时提供。

类型lambda技巧利用了类型位置中的空块创建匿名结构类型的事实。然后我们使用#语法来获取类型成员。

在某些情况下,您可能需要更复杂的类型lambda,这些类型lambda在内联编写时很难编写。这是我今天从我的代码中摘录的一个例子:

// types X and E are defined in an enclosing scope
private[iteratee] class FG[F[_[_], _], G[_]] {
  type FGA[A] = F[G, A]
  type IterateeM[A] = IterateeT[X, E, FGA, A] 
}

这个类存在,这样我就可以使用类似FG [F,G]#IterateeM这样的名称来引用特定于某个变换器版本的第二个单子的单调函数specialized到一些第三个单子的类型。当您开始堆叠时,这些构造变得非常必要。当然,我从不实例化FG; 它只是一种Hack,让我在类型系统中表达我想要的内容。

0