Per mettere le cose nel contesto: questa risposta è stata originariamente pubblicata in un'altra discussione. Lo vedi qui perché i due thread sono stati uniti. La dichiarazione della domanda in detto thread era la seguente:
Come risolvere questa definizione di tipo: Pure [({type? [A] = (R, a)}) #?]?
Quali sono i motivi per utilizzare tale costruzione?
Snipped viene dalla libreria 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)
}
//...
}
Risposta:
trait Pure[P[_]] {
def pure[A](a: => A): P[A]
}
Il carattere di sottolineatura nelle caselle dopo Pimplica che si tratta di un costruttore di tipo accetta un tipo e ne restituisce un altro. Esempi di costruttori di tipi con questo tipo: List, Option.
Dai Listun Inttipo concreto e ti dà List[Int]un altro tipo concreto. Dai Listun Stringe ti dà List[String]. Eccetera.
Così, List, Optionpossono essere considerate funzioni di livello tipo di arietà 1. Formalmente diciamo, hanno una sorta * -> *. L'asterisco indica un tipo.
Ora Tuple2[_, _]è un costruttore di tipi con kind (*, *) -> *cioè devi dargli due tipi per ottenere un nuovo tipo.
Dal momento che le loro firme non corrispondono, non è possibile sostituire Tuple2per P. Quello che devi fare è applicare parzialmente Tuple2 su uno dei suoi argomenti, che ci darà un costruttore di tipi con kind * -> *, e possiamo sostituirlo P.
Sfortunatamente Scala non ha una sintassi speciale per l'applicazione parziale dei costruttori di tipi, e quindi dobbiamo ricorrere alla mostruosità chiamata tipo lambdas. (Quello che hai nel tuo esempio.) Si chiamano così perché sono analoghi alle espressioni lambda che esistono a livello di valore.
Il seguente esempio potrebbe essere d'aiuto:
// 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>
Modificare:
Più parallelismi a livello di valore e di livello.
// 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>
Nel caso che hai presentato, il parametro type Rè locale per funzionare Tuple2Puree quindi non puoi semplicemente definirlo type PartialTuple2[A] = Tuple2[R, A], perché semplicemente non c'è posto dove puoi mettere quel sinonimo.
Per affrontare un caso del genere, utilizzo il seguente trucco che utilizza membri di tipo. (Speriamo che l'esempio sia autoesplicativo.)
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)]