Per mettere le cose nel contesto: questa risposta è stata originariamente pubblicata in un'altra discussione. Lo stai vedendo qui perché i due thread sono stati uniti. La dichiarazione domanda nel detto filo è stato il seguente:
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)
}
//...
}
Risposta:
trait Pure[P[_]] {
def pure[A](a: => A): P[A]
}
Quello sottolineatura nelle caselle dopo P
implica che si tratta di un costruttore di tipo richiede un tipo e restituisce un altro tipo. Esempi di costruttori di tipi con questo tipo: List
, Option
.
Dare List
un Int
, un tipo concreto e ti dà List[Int]
, un altro tipo concreto. Dare List
a String
e ti dà List[String]
. Etc.
Quindi, List
, Option
possono essere considerati funzioni di livello tipo di cortesia 1. Formalmente diciamo che hanno un tipo * -> *
. L'asterisco indica un tipo.
Ora Tuple2[_, _]
è un tipo di costruttore con tipo (*, *) -> *
, ad esempio è necessario dargli due tipi per ottenere un nuovo tipo.
Poiché le loro firme non corrispondono, non è possibile sostituire Tuple2
per P
. Quello che devi fare è parzialmente applicareTuple2
su uno dei suoi argomenti, che ci fornirà un costruttore di tipo con il tipo * -> *
, e possiamo sostituirlo con P
.
Purtroppo Scala non ha una sintassi speciale per l'applicazione parziale dei costruttori di tipi, e quindi dobbiamo ricorrere alla mostruosità chiamata tipo lambda. (Quello che hai nel tuo esempio.) Sono chiamati così perché sono analoghi alle espressioni lambda che esistono a livello di valore.
L'esempio che segue potrebbe aiutare:
// 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>
Modifica:
livello più valore e il livello di tipo parallelo.
// 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 avete presentato, il parametro di tipo R
è locale a funzionare Tuple2Pure
e quindi non si può semplicemente definire type PartialTuple2[A] = Tuple2[R, A]
, semplicemente perché non c'è nessun posto dove si può mettere quello sinonimo.
Per risolvere un caso del genere, utilizzo il seguente trucco che utilizza i membri di tipo. (Si spera che l'esempio sia auto-esplicativo.)
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)]
Vedere [anche] (https://underscore.io/blog/posts/2016/12/05/type-lambdas.html). –