Para poner las cosas en contexto: Esta respuesta se publicó originalmente en otro hilo. Lo está viendo aquí porque los dos hilos se han fusionado. La declaración pregunta en el dicho hilo fue el siguiente:
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)
}
//...
}
Respuesta:
trait Pure[P[_]] {
def pure[A](a: => A): P[A]
}
El subrayado en las cajas después de P
implica que es un tipo constructor toma un tipo y devuelve otro tipo. Ejemplos de constructores de tipo con este tipo: List
, Option
.
Proporcione List
un Int
, un tipo concreto, y le da List[Int]
, otro tipo concreto. Dé List
a String
y le da List[String]
. Etc.
Por lo tanto, List
, Option
pueden considerarse funciones de nivel de tipo de arity 1. Formalmente decimos que tienen un tipo * -> *
. El asterisco denota un tipo.
Ahora Tuple2[_, _]
es un constructor de tipo con tipo (*, *) -> *
es decir, necesita darle dos tipos para obtener un nuevo tipo.
Dado que sus firmas no coinciden, no puede sustituir Tuple2
por P
. Lo que debe hacer es aplicar parcialmenteTuple2
en uno de sus argumentos, lo que nos dará un constructor de tipo con el tipo * -> *
, y podemos sustituirlo por P
.
Desafortunadamente, Scala no tiene una sintaxis especial para la aplicación parcial de constructores de tipo, por lo que debemos recurrir a la monstruosidad denominada tipo lambdas. (Lo que tienes en tu ejemplo.) Se llaman así porque son análogas a las expresiones lambda que existen a nivel de valor.
El ejemplo siguiente podría ayudar:
// 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>
Editar:
Más nivel de valor y de nivel de tipo paralelos.
// 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>
En el caso de que haya presentado, el parámetro de tipo R
es local para funcionar Tuple2Pure
y así no se puede simplemente definir type PartialTuple2[A] = Tuple2[R, A]
, porque simplemente no hay lugar donde se puede poner ese sinónimo.
Para tratar este caso, utilizo el siguiente truco que hace uso de los miembros de tipo. (Afortunadamente el ejemplo es autoexplicativo.)
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)]
Ver [también] (https://underscore.io/blog/posts/2016/12/05/type-lambdas.html). –