Scala 中的类型 lambda 是什么,它们有什么好处?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8736164/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What are type lambdas in Scala and what are their benefits?
提问by ron
Sometime I stumble into the semi-mysterious notation of
有时我偶然发现了半神秘的符号
def f[T](..) = new T[({type l[A]=SomeType[A,..]})#l] {..}
in Scala blog posts, which give it a "we used that type-lambda trick" handwave.
在 Scala 博客文章中,这给了它一个“我们使用了那种类型 lambda 技巧”的手波。
While I have some intutition about this (we gain an anonymous type parameter Awithout having to pollute the definition with it?), I found no clear source describing what the type lambda trick is, and what are its benefits. Is it just syntactic sugar, or does it open some new dimensions?
虽然我对此有一些直觉(我们获得了一个匿名类型参数A而不必用它来污染定义?),但我没有找到描述 lambda 类型技巧是什么及其好处的明确来源。它只是语法糖,还是打开了一些新的维度?
采纳答案by Kris Nuttycombe
Type lambdas are vital quite a bit of the time when you are working with higher-kinded types.
当您使用更高级的类型时,类型 lambda 很重要。
Consider a simple example of defining a monad for the right projection of Either[A, B]. The monad typeclass looks like this:
考虑一个简单的例子,定义一个 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]
}
Now, Either is a type constructor of two arguments, but to implement Monad, you need to give it a type constructor of one argument. The solution to this is to use a type lambda:
现在,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]
}
This is an example of currying in the type system - you have curried the type of Either, such that when you want to create an instance of EitherMonad, you have to specify one of the types; the other of course is supplied at the time you call point or bind.
这是类型系统中柯里化的一个例子——你已经柯里化了Either的类型,这样当你想创建一个EitherMonad的实例时,你必须指定其中一种类型;另一个当然是在您调用 point 或 bind 时提供的。
The type lambda trick exploits the fact that an empty block in a type position creates an anonymous structural type. We then use the # syntax to get a type member.
类型 lambda 技巧利用了这样一个事实,即类型位置中的空块会创建匿名结构类型。然后我们使用# 语法来获取类型成员。
In some cases, you may need more sophisticated type lambdas that are a pain to write out inline. Here's an example from my code from today:
在某些情况下,您可能需要更复杂的类型 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]
}
This class exists exclusively so that I can use a name like FG[F, G]#IterateeM to refer to the type of the IterateeT monad specialized to some transformer version of a second monad which is specialized to some third monad. When you start to stack, these kinds of constructs become very necessary. I never instantiate an FG, of course; it's just there as a hack to let me express what I want in the type system.
这个类是专门存在的,所以我可以使用像 FG[F, G]#IterateeM 这样的名字来引用 IterateeT monad 的类型,该类型专用于第二个 monad 的某个转换器版本,该版本专用于某个第三个 monad。当您开始堆叠时,这些类型的构造变得非常必要。当然,我从不实例化 FG;它只是让我在类型系统中表达我想要的东西。
回答by retronym
The benefits are exactly the same as those conferred by anonymous functions.
好处与匿名函数赋予的好处完全相同。
def inc(a: Int) = a + 1; List(1, 2, 3).map(inc)
List(1, 2, 3).map(a => a + 1)
An example usage, with Scalaz 7. We want to use a Functorthat can map a function over the second element in a Tuple2.
使用 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 provides some implicit conversions that can infer the type argument to Functor, so we often avoid writing these altogether. The previous line can be rewritten as:
Scalaz 提供了一些隐式转换,可以将类型参数推断为Functor,因此我们经常避免完全编写这些转换。上一行可以改写为:
(1, 2).map(a => a + 1) // (1, 3)
If you use IntelliJ, you can enable Settings, Code Style, Scala, Folding, Type Lambdas. This then hides the crufty parts of the syntax, and presents the more palatable:
如果您使用 IntelliJ,则可以启用设置、代码样式、Scala、折叠、类型 Lambda。这然后隐藏了语法的粗糙部分,并呈现出更可口的部分:
Functor[[a]=(Int, a)].map((1, 2))(a => a + 1)) // (1, 3)
A future version of Scala might directly support such a syntax.
Scala 的未来版本可能会直接支持这种语法。
回答by missingfaktor
To put things in context: This answer was originally posted in another thread. You are seeing it here because the two threads have been merged. The question statement in the said thread was as follows:
把事情放在上下文中:这个答案最初发布在另一个线程中。您在这里看到它是因为两个线程已合并。所述线程中的问题陈述如下:
How to resolve this type definition: Pure[({type ?[a]=(R, a)})#?] ?
What are the reasons of using such construction?
Snipped comes from scalaz library:
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) } //... }
如何解决这个类型定义: Pure[({type ?[a]=(R, a)})#?] ?
使用这种结构的原因是什么?
截图来自 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) } //... }
Answer:
回答:
trait Pure[P[_]] {
def pure[A](a: => A): P[A]
}
The one underscore in the boxes after Pimplies that it is a type constructor takes one type and returns another type. Examples of type constructors with this kind: List, Option.
后面框中的下划线P表示它是一个类型构造函数,它接受一种类型并返回另一种类型。具有这种类型的类型构造函数的示例:List, Option.
Give Listan Int, a concrete type, and it gives you List[Int], another concrete type. Give Lista Stringand it gives you List[String]. Etc.
给List一个Int,一个具体的类型,它给你List[Int],另一个具体的类型。给List一个String,它给你List[String]。等等。
So, List, Optioncan be considered to be type level functions of arity 1. Formally we say, they have a kind * -> *. The asterisk denotes a type.
所以, List,Option可以被认为是arity 1的类型级函数。我们正式地说,它们有一个种类* -> *。星号表示类型。
Now Tuple2[_, _]is a type constructor with kind (*, *) -> *i.e. you need to give it two types to get a new type.
现在Tuple2[_, _]是一个带有 kind 的类型构造函数,(*, *) -> *即你需要给它两种类型来获得一个新类型。
Since their signatures do not match, you cannot substitute Tuple2for P. What you need to do is partially applyTuple2on one of its arguments, which will give us a type constructor with kind * -> *, and we can substitue it for P.
由于他们的签名不匹配,你不能代替Tuple2的P。您需要做的是部分应用Tuple2它的一个参数,这将为我们提供一个带有 kind 的类型构造函数* -> *,我们可以将其替换为P。
Unfortunately Scala has no special syntax for partial application of type constructors, and so we have to resort to the monstrosity called type lambdas. (What you have in your example.) They are called that because they are analogous to lambda expressions that exist at value level.
不幸的是,Scala 没有用于部分应用类型构造函数的特殊语法,因此我们不得不求助于称为类型 lambda 的怪物。(您的示例中有什么。)之所以这样称呼它们,是因为它们类似于存在于值级别的 lambda 表达式。
The following example might help:
以下示例可能会有所帮助:
// 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>
Edit:
编辑:
More value level and type level parallels.
更多的价值级别和类型级别相似。
// 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>
In the case you have presented, the type parameter Ris local to function Tuple2Pureand so you cannot simply define type PartialTuple2[A] = Tuple2[R, A], because there is simply no place where you can put that synonym.
在您提供的情况下,类型参数R对于函数来说是局部的Tuple2Pure,因此您不能简单地定义type PartialTuple2[A] = Tuple2[R, A],因为根本没有地方可以放置该同义词。
To deal with such a case, I use the following trick that makes use of type members. (Hopefully the example is self-explanatory.)
为了处理这种情况,我使用了以下利用类型成员的技巧。(希望这个例子是不言自明的。)
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)]
回答by wiesiu_p
type World[M[_]] = M[Int]causes that whatever we put in Ain X[A]the implicitly[X[A] =:= Foo[String,Int]]is always true I guess.
type World[M[_]] = M[Int]导致,无论我们把A在X[A]的implicitly[X[A] =:= Foo[String,Int]]永远是真实的,我猜。

