什么是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构造函数的例子: List
, Option
。
给List
一个Int
,一个具体的types,它给你List[Int]
,另一个具体的types。 给List
一个String
,它给你List[String]
。 等等。
所以, List
, Option
可以被认为是arity 1的types级函数。我们正式地说,他们有一种* -> *
。 星号表示types。
现在, Tuple2[_, _]
是types构造函数(*, *) -> *
即你需要给它两种types来获得一个新types。
由于它们的签名不匹配,所以不能用Tuple2
replaceTuple2
你需要做的是在它的一个参数上部分应用 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]
中的A
中implicitly[X[A] =:= Foo[String,Int]]
都是真的。