Как правильно определить тип лямбда?
Я использовал =:=
как пример типа лямбда для создания простого минимального примера.
=:=
введите два аргумента, я хотел бы сделать один на уровне уровня.
Я принимаю наивную реализацию type Curry[G] = {type l[L] = L =:= G}
, но в практических целях он вызывает ошибки:
type X = Int
type Y = Int
type CurryInt[T] = T =:= Int
type Curry[G] = {type l[L] = L =:= G}
type CurrStatic = {type l[L] = L =:= Int}
object CurryObj {type l[L] = L =:= Int}
trait Apply[P[_], T]
implicit def liftApply[P[_], T](implicit ev : P[T]) = new Apply[P,T] {}
implicitly[Apply[CurryInt, Y]] // ok
implicitly[Apply[Curry[X]#l, Y]] // fails
implicitly[Apply[Curry[X]#l, Y]](liftApply) // fails
implicitly[Apply[Curry[X]#l, Y]](liftApply[Curry[X]#l, Y]) // ok
implicitly[Apply[CurrStatic#l, Y]] // fails
implicitly[Apply[CurryObj.l, Y]] // ok
Здесь выводится вывод типа. Как определить тип lambdas, чтобы он работал?
Ответы
Ответ 1
Рассмотрим эту упрощенную версию вашего примера:
trait Secret
type Curry = { type l[L] = Secret }
def foo[P[_], T](ev : P[T]) = ???
val bar: Curry#l[Int] = ???
foo(bar)
При вызове foo
значение bar
имеет просто тип Secret
, компилятор не знает, откуда приходит ваш конкретный Secret
.
Ваше значение bar
- это просто Secret
, и оно не поддерживает информацию, указывающую на Curry#l[Int]
.
Компилятор не может заключить, что P => Curry#l
и T => Int
.
Компилятор видит только Secret
и теряет контекст Curry#l
, несмотря на аннотирование типа Curry#l[Int]
вместо Secret
.
Другой пример (исходящий из этого вопроса), подвергая аналогичное поведение:
trait Curry { type l }
trait CurryB extends Curry { type l = String }
def foo[P <: Curry](x: P#l) = ???
val bar: CurryB#l = ???
foo(bar)
CurryObj
ситуация другая, считают, что CurryInt#l
, Curry#l
и CurrStatic#l
являются просто псевдонимами типа. CurryObj.l
, вместо этого, является фактическим типом, частью конкретного объекта CurryObj
.
Посмотрим на это (REPL):
scala> trait Secret
defined trait Secret
scala> type Curry = { type l[L] = Secret }
defined type alias Curry
scala> object CurryObj { type l[L] = Secret }
defined object CurryObj
scala> object S extends Secret
defined object S
scala> val foo0: Curry#l[Int] = S
foo0: Secret = [email protected]
scala> val foo1: CurryObj.l[Int] = S
foo1: CurryObj.l[Int] = [email protected]
Обратите внимание, что псевдоним типа Curry#l[Int]
→ Secret
разрешен немедленно, вместо этого сохраняется фактический тип CurryObj.l[Int]
.
Ответ 2
немного больше подробностей, но компилирует:) (scala 2.12.3)
type X = Int
type Y = Int
type CurryInt[T] = T =:= Int
type Curry[G] = {type l[L] = =:=[L, G]}
type CurrStatic = {type l[L] = L =:= Int}
object CurryObj {type l[L] = L =:= Int}
trait Apply[P[_], T]
implicit def liftApply[P[_], T](implicit ev : P[T]) = new Apply[P,T] {}
type L1[R] = =:=[R, X]
type L2[R] = =:=[R, Int]
implicitly[Apply[CurryInt, Y]] // ok
implicitly[Apply[L1, Y]] // ok
implicitly[Apply[L1, Y]](liftApply[L1, Y]) // ok
implicitly[Apply[Curry[X]#l, Y]](liftApply[Curry[X]#l, Y]) // ok
implicitly[Apply[L2, Y]] // ok
implicitly[Apply[CurryObj.l, Y]] // ok
Ответ 3
Похоже, что компилятор scala не смог обработать голые типы из прогнозов типов. Я проследил вывод -Ytyper-debug
и обнаружил, что вся необходимая информация о типе уносится, но отклоняется без видимой причины. Но все еще можно получить выражения для выражения lambdas типа внутри черты. Этот ответ дал мне представление о решении.
type X = Int
type Y = Int
trait Wrap {
type l[T]
}
trait GenWrap[W[_]] extends Wrap {
final type l[T] = W[T]
}
type CInt[T] = T =:= Int
class CurryInt extends Wrap {type l[T] = T =:= Int}
class Curry[U] extends Wrap {type l[T] = T =:= U}
type TCurry[U] = Wrap {type l[T] = T =:= U}
trait Apply[W <: Wrap, T]
implicit def lift[W <: Wrap, T](implicit ev : W#l[T]) = new Apply[W,T] {}
implicitly[Apply[CurryInt, Y]]
implicitly[Apply[Curry[X], Y]]
implicitly[Apply[TCurry[X], Y]]
implicitly[Apply[GenWrap[CInt], Y]]