Чтобы поместить вещи в контекст: Этот ответ был первоначально размещен в другой теме. Вы видите это здесь, потому что два потока были объединены. Постановка вопроса в указанной ветке была следующей:
Как решить это определение типа: Pure [({type? [A] = (R, a)}) #?]?
Каковы причины использования такой конструкции?
Snipped поставляется из библиотеки скалаза:
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.
Дайте , тип бетона, и это дает вам , еще один конкретный тип. Дайте и это дает вам . И т.п.ListIntList[Int]ListStringList[String]
Таким образом, List, Optionможно рассматривать как функции уровня типа арности 1. Формально мы говорим, что они имеют вид * -> *. Звездочка обозначает тип.
Теперь Tuple2[_, _]это конструктор типа с типом, (*, *) -> *т.е. вам нужно дать ему два типа, чтобы получить новый тип.
Так как их подпись не совпадает, вы не можете заменить Tuple2на P. Что вам нужно сделать, так это частично применить Tuple2 к одному из его аргументов, что даст нам конструктор типа с видом * -> *, и мы можем заменить его P.
К сожалению, в Scala нет специального синтаксиса для частичного применения конструкторов типов, поэтому нам приходится прибегать к чудовищу, называемому типом lambdas. (Что вы имеете в своем примере.) Они называются так, потому что они аналогичны лямбда-выражениям, которые существуют на уровне значений.
Следующий пример может помочь:
// 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)]