什么是Scala中的lambdatypes,它们有什么好处?

有时我偶然发现了这个半神秘的符号

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

在斯卡拉的博客文章中,给它一个“我们用过那种types的拉姆达技巧”的手摇。

虽然我有一些这样的说法(我们获得一个匿名types参数A而不必污染它的定义?),我发现没有明确的来源描述什么types的lambda技巧是什么,它有什么好处。 这只是句法糖,还是开辟了一些新的维度?

typeslambda在您使用更高级别的types时很重要。

考虑一个简单的例子,为任一[A,B]的右投影定义monad。 Monadtypes类似于这样:

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

现在,无论是两个参数的types构造函数,但要实现Monad,您需要给它一个参数的types构造函数。 解决scheme是使用一个typeslambda:

 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] } 

这是一个在types系统中进行曲调的例子 – 你已经使用了Either的types,这样当你想创build一个EitherMonad的实例时,你必须指定一个types; 另一个当然是在你呼叫点或绑定时提供的。

lambdatypes的技巧利用了types位置上的空块创build匿名结构types的事实。 然后我们使用#语法来获得一个types成员。

在某些情况下,您可能需要更复杂的typeslambda,这是一种痛苦的内联写法。 以下是我今天的代码示例:

 // 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这样的名称来引用IterateeT monad的types,专用于专用于第三个monad的第二个monad的变换器版本。 当你开始堆砌时,这些构造变得非常必要。 当然,我从来没有实例化一个FG。 它只是在那里让我expression我想要的types系统。

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

 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的types参数推断出来,所以我们经常避免把它们写出来。 上一行可以被重写为:

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

如果你使用IntelliJ,你可以启用设置,代码风格,斯卡拉,折叠,types兰姆达斯。 这隐藏了语法的重要部分 ,并提出更可口:

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

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

把事情放在上下文中:这个答案最初是在另一个线程中发布的。 你在这里看到它,因为两个线程已经合并。 上述话题中的问题陈述如下:

如何解决这个types定义:Pure [({type?[a] =(R,a)})#?]?

使用这种build筑的原因是什么?

剪报来自斯卡拉斯图书馆:

 trait Pure[P[_]] { def pure[A](a: => A): P[A] } object Pure { import Scalaz._ //... implicit def Tuple2Pure[R: Zero]: Pure[({type ?[a]=(R, a)})#?] = new Pure[({type ?[a]=(R, a)})#?] { def pure[A](a: => A) = (Ø, a) } //... } 

回答:

 trait Pure[P[_]] { def pure[A](a: => A): P[A] } 

P后面的那个下划线意味着它是一个types构造函数需要一个types并返回另一个types。 这种types构造函数的例子: ListOption

List一个Int ,一个具体的types,它给你List[Int] ,另一个具体的types。 给List一个String ,它给你List[String] 。 等等。

所以, ListOption可以被认为是arity 1的types级函数。我们正式地说,他们有一种* -> * 。 星号表示types。

现在, Tuple2[_, _]是types构造函数(*, *) -> *即你需要给它两种types来获得一个新types。

由于它们的签名不匹配,所以不能用Tuple2replaceTuple2 你需要做的是在它的一个参数上部分应用 Tuple2 ,这会给我们一个types为* -> *构造函数,我们可以用它替代P

不幸的是,Scala对types构造函数的部分应用没有特殊的语法,所以我们不得不求助于lambdastypes的怪物。 (你在你的例子中有什么)。它们被称为是因为它们类似于在值级别存在的lambdaexpression式。

以下示例可能有所帮助:

 // VALUE LEVEL // foo has signature: (String, String) => String scala> def foo(x: String, y: String): String = x + " " + y foo: (x: String, y: String)String // world wants a parameter of type String => String scala> def world(f: String => String): String = f("world") world: (f: String => String)String // So we use a lambda expression that partially applies foo on one parameter // to yield a value of type String => String scala> world(x => foo("hello", x)) res0: String = hello world // TYPE LEVEL // Foo has a kind (*, *) -> * scala> type Foo[A, B] = Map[A, B] defined type alias Foo // World wants a parameter of kind * -> * scala> type World[M[_]] = M[Int] defined type alias World // So we use a lambda lambda that partially applies Foo on one parameter // to yield a type of kind * -> * scala> type X[A] = World[({ type M[A] = Foo[String, A] })#M] defined type alias X // Test the equality of two types. (If this compiles, it means they're equal.) scala> implicitly[X[Int] =:= Foo[String, Int]] res2: =:=[X[Int],Foo[String,Int]] = <function1> 

编辑:

更多的价值水平和types水平相似。

 // VALUE LEVEL // Instead of a lambda, you can define a named function beforehand... scala> val g: String => String = x => foo("hello", x) g: String => String = <function1> // ...and use it. scala> world(g) res3: String = hello world // TYPE LEVEL // Same applies at type level too. scala> type G[A] = Foo[String, A] defined type alias G scala> implicitly[X =:= Foo[String, Int]] res5: =:=[X,Foo[String,Int]] = <function1> scala> type T = World[G] defined type alias T scala> implicitly[T =:= Foo[String, Int]] res6: =:=[T,Foo[String,Int]] = <function1> 

在你提出的情况下,types参数R对于函数Tuple2Pure是局部的,所以你不能简单地定义type PartialTuple2[A] = Tuple2[R, A] ,因为根本没有地方可以放置同义词。

为了处理这种情况,我使用了以下利用types成员的技巧。 (希望这个例子是不言自明的。)

 scala> type Partial2[F[_, _], A] = { | type Get[B] = F[A, B] | } defined type alias Partial2 scala> implicit def Tuple2Pure[R]: Pure[Partial2[Tuple2, R]#Get] = sys.error("") Tuple2Pure: [R]=> Pure[[B](R, B)] 

我猜,无论我们放在X[A]中的Aimplicitly[X[A] =:= Foo[String,Int]]都是真的。