有时我偶然发现了
的半神秘符号def f[T](..) = new T[({type l[A]=SomeType[A,..]})#l] {..}
在Scala博客文章中,它给出了“我们使用那种类型-lambda技巧”的手动波。
虽然我对此有一些说明(我们获得了一个匿名类型参数A
而不必用它来污染定义?),我发现没有明确的来源描述lambda类型的类型是什么,它是什么好处。它只是语法糖,还是开了一些新的维度?
答案 0 :(得分:145)
当你使用更高级别的类型时,类型lambda是非常重要的。
考虑一个简单的例子,为Aither [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]
}
现在,要么是两个参数的类型构造函数,要实现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]
}
这是在类型系统中进行curry的示例 - 您已经知道了Either的类型,这样当您想要创建EitherMonad的实例时,您必须指定其中一种类型;另一方面当然是在您调用point或bind时提供的。
类型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这样的名称来引用IterateeT monad的类型,该monadrateeT monad专用于第二个monad的某些变换器版本,专门用于某些第三个monad。当你开始堆叠时,这些类型的构造变得非常必要。当然,我从未实例化过FG;它只是让我在类型系统中表达我想要的东西。
答案 1 :(得分:51)
这些好处与匿名函数赋予的好处完全相同。
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,则可以启用“设置”,“代码样式”,“Scala”,“折叠”,“键入Lambdas”。然后是hides the crufty parts of the syntax,并且呈现出更可口的:
Functor[[a]=(Int, a)].map((1, 2))(a => a + 1)) // (1, 3)
Scala的未来版本可能会直接支持这种语法。
答案 2 :(得分:40)
把事情放在上下文中:这个答案最初是在另一个帖子中发布的。你在这里看到它,因为两个线程已经合并。上述主题中的问题陈述如下:
如何解决此类型定义:纯[({type?[a] =(R,a)})#?]?
使用这种结构的原因是什么?
Snipped来自scalaz库:
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
之后的方框中的一个下划线暗示它是一个类型构造函数,它接受一种类型并返回另一种类型。此类型构造函数的示例:List
,Option
。
给List
Int
一个具体类型,它给你List[Int]
,另一个具体类型。给List
一个String
,它会为您提供List[String]
。等
因此,List
,Option
可以被认为是arity 1的类型级函数。我们正式说,它们有一种* -> *
。星号表示一种类型。
现在Tuple2[_, _]
是一个类型构造函数,其类型为(*, *) -> *
,即您需要为其提供两种类型才能获得新类型。
由于他们的签名不匹配,因此您无法将Tuple2
替换为P
。您需要做的是在其中一个参数上部分应用 Tuple2
,这将为我们提供类型* -> *
的类型构造函数,我们可以将其替换为{{1 }}
不幸的是,Scala没有用于部分应用类型构造函数的特殊语法,因此我们必须求助于称为类型lambdas的怪物。 (你的例子中有什么。)它们被称为因为它们类似于存在于价值层面的lambda表达式。
以下示例可能有所帮助:
P
修改强>
更多价值水平和类型水平相似。
// 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>
在您提交的案例中,类型参数// 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>
是函数R
的本地参数,因此您无法简单地定义Tuple2Pure
,因为根本没有可以放置它的地方同义词。
为了处理这种情况,我使用了以下使用类型成员的技巧。 (希望这个例子不言自明。)
type PartialTuple2[A] = Tuple2[R, A]
答案 3 :(得分:0)
type World[M[_]] = M[Int]
导致无论我们在A
X[A]
implicitly[X[A] =:= Foo[String,Int]]
中放置{{1}}总是如此,我猜。