Um die Dinge in einen Zusammenhang zu bringen: Diese Antwort wurde ursprünglich in einem anderen Thread veröffentlicht. Sie sehen es hier, weil die beiden Threads zusammengeführt wurden. Die Fragestellung in diesem Thread lautete wie folgt:
So lösen Sie diese Typdefinition auf: Pure [({type? [A] = (R, a)}) #?]?
Was sind die Gründe für die Verwendung einer solchen Konstruktion?
Snipped stammt aus der Scalaz-Bibliothek:
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)
}
//...
}
Antworten:
trait Pure[P[_]] {
def pure[A](a: => A): P[A]
}
Der eine Unterstrich in den Feldern danach P
impliziert, dass es sich um einen Typkonstruktor handelt, der einen Typ verwendet und einen anderen Typ zurückgibt. Beispiele für Typkonstruktoren dieser Art: List
, Option
.
Geben Sie List
ein Int
, einen konkreten Typ, und es gibt Ihnen einen List[Int]
weiteren konkreten Typ. Gib List
ein String
und es gibt dir List[String]
. Etc.
Also List
, Option
kann in Betracht gezogen werden Typ - Level - Funktionen von arity sein 1. Formal wir sagen, sie haben eine Art * -> *
. Das Sternchen kennzeichnet einen Typ.
Jetzt Tuple2[_, _]
ist ein Typkonstruktor mit kind, (*, *) -> *
dh Sie müssen ihm zwei Typen geben, um einen neuen Typ zu erhalten.
Da ihre Signaturen nicht übereinstimmen, können Sie nicht ersetzen Tuple2
für P
. Was Sie tun müssen, ist teilweise Tuple2
auf eines seiner Argumente anzuwenden , wodurch wir einen Typkonstruktor mit kind erhalten * -> *
, und wir können ihn ersetzen P
.
Leider hat Scala keine spezielle Syntax für die teilweise Anwendung von Typkonstruktoren, weshalb wir auf die Monstrosität zurückgreifen müssen, die als Typ Lambdas bezeichnet wird. (Was Sie in Ihrem Beispiel haben.) Sie werden so genannt, weil sie analog zu Lambda-Ausdrücken sind, die auf Wertebene existieren.
Das folgende Beispiel könnte helfen:
// 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>
Bearbeiten:
Weitere Parallelen auf Wertebene und Typenebene.
// 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 dem von Ihnen vorgestellten Fall ist der Typparameter R
für die Funktion lokal Tuple2Pure
und kann daher nicht einfach definiert werden type PartialTuple2[A] = Tuple2[R, A]
, da es einfach keinen Ort gibt, an dem Sie dieses Synonym einfügen können.
Um mit einem solchen Fall umzugehen, verwende ich den folgenden Trick, bei dem Typelemente verwendet werden. (Hoffentlich ist das Beispiel selbsterklärend.)
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)]