把事情放在上下文中:这个答案最初发布在另一个线程中。您在这里看到它是因为这两个线程已合并。该线程中的问题陈述如下:
如何解析这种类型定义: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)
}
//...
}
答案:
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 在它的一个参数上,这将为我们提供一个类型为* -> * 的类型构造函数,我们可以用它替换P。
不幸的是,Scala 没有用于类型构造函数的部分应用的特殊语法,因此我们不得不求助于称为 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>
编辑:
更多的价值级别和类型级别的平行。
// 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],因为根本没有地方可以放置该同义词。
为了处理这种情况,我使用了以下利用类型成员的技巧。 (希望这个例子是不言自明的。)
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)]