Scala中的lambdas类型是什么?它们的优点是什么?

Scala中的lambdas类型是什么?它们的优点是什么?,scala,types,Scala,Types,有时我偶然发现了一种半神秘的符号 def f[T](..) = new T[({type l[A]=SomeType[A,..]})#l] {..} 在Scala的博客文章中,我们使用了lambda技巧handwave 虽然我对此有一些直觉,但我们获得了一个匿名类型参数A,而不必用它污染定义?我发现没有明确的来源来描述什么是类型lambda技巧,以及它的好处是什么。它只是语法上的甜点,还是打开了一些新的维度?类型lambda在处理高级类型时相当重要 考虑一个简单的例子,为[a,B]的右投影定

有时我偶然发现了一种半神秘的符号

def f[T](..) = new T[({type l[A]=SomeType[A,..]})#l] {..} 
在Scala的博客文章中,我们使用了lambda技巧handwave


虽然我对此有一些直觉,但我们获得了一个匿名类型参数A,而不必用它污染定义?我发现没有明确的来源来描述什么是类型lambda技巧,以及它的好处是什么。它只是语法上的甜点,还是打开了一些新的维度?

类型lambda在处理高级类型时相当重要

考虑一个简单的例子,为[a,B]的右投影定义一个单子。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的示例-您已经使用了其中一种类型,因此当您想要创建EitherMonad的实例时,您必须指定其中一种类型;另一个当然是在调用点或绑定时提供的

类型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单子的类型,第二个单子专门化为第三个单子。当您开始堆叠时,这些类型的构造变得非常必要。当然,我从不实例化FG;它只是一个黑客,让我在类型系统中表达我想要的内容。

其好处与匿名函数所赋予的好处完全相同

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

List(1, 2, 3).map(a => a + 1)
使用Scalaz 7的示例用法。我们想使用一个函子,它可以将一个函数映射到Tuple2中的第二个元素上

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

(1, 2).map(a => a + 1) // (1, 3)
如果您使用IntelliJ,则可以启用设置、代码样式、Scala、折叠、键入Lambdas。然后,这将呈现更美味的:

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

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

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

如何解析此类型定义:Pure[{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)
  }

//...
}
答复:

p后面的框中的一条下划线表示它是一个类型构造函数,它接受一种类型并返回另一种类型。具有这种类型的类型构造函数的示例:列表、选项

给List一个Int,一个具体类型,它会给你List[Int],另一个具体类型。给List一个字符串,它会给你List[String]。等等

所以,List,Option可以看作是arity 1的类型级函数。正式地说,他们有一种*->*。星号表示一种类型

现在,Tuple2[u,u]是一个类型构造函数,其种类为*,*->*,也就是说,您需要给它两种类型才能得到一个新类型

由于它们的签名不匹配,您不能用Tuple2代替p。您需要做的是在它的一个参数上部分应用Tuple2,这将给我们一个kind*->*的类型构造函数,我们可以用它代替p

不幸的是,Scala对于类型构造函数的部分应用没有特殊的语法,因此我们不得不求助于称为类型lambdas的怪物。你的例子里有什么。之所以称之为lambda,是因为它们类似于存在于值级别的lambda表达式

以下示例可能会有所帮助:

// 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>

键入World[M[\u]]=M[Int]会导致无论我们在X[A]中放入什么,隐式地[X[A]=:=Foo[String,Int]]都是真的,我想。

最后一个片段看起来真的很不错。IntelliJ scala插件确实很棒!谢谢上一个示例中可能缺少lambda。另外,为什么元组函子选择转换最后一个值?这是惯例/实际默认值吗?我正在为Nika运行夜间睡眠,我没有描述的想法选项。有趣的是,对应用类型Lambda的检查可以简化。它被移动到设置->编辑器->代码折叠。@retronym,我在REPL:`:11:error:value map不是Int的成员,Int 1,2.mapa=>a+1^`中尝试1,2.mapa=>a+1^`时出错,值得注意的是,虽然一些新类型的黑客,例如TypeCompose库有一些方法可以绕过这一点。我很想看到你
为EitherMonad类定义绑定方法。:-除此之外,如果我可以在这里给Adriaan介绍一下,在这个例子中,您没有使用更高级的类型。你在FG,但不在EitherMonad。相反,您使用的是类型构造函数,其种类为*=>*。这种是1阶,并不高。我以为那种*是1阶,但在任何情况下Monad都有种*=>*=>*。另外,您会注意到我指定了[A,B]的正确投影-实现很简单,但是如果您以前没有这样做过,这是一个很好的练习!I gues Daniel不调用*=>*高阶函数的观点是合理的,因为我们不调用将非函数映射到非函数的普通函数,换句话说,普通值映射到普通值的高阶函数。Pierce的TAPL书,第442页:使用类似*⇒*⇒* 称为高阶类型运算符。请参阅。
trait Pure[P[_]] {
  def pure[A](a: => A): P[A]
}
// 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>
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)]