Reputation: 5977
I used =:=
as example type lambda for purpose of making simple minimal example.
=:=
type take two arguments, I'd like to curry one at type level.
I take naive implementation type Curry[G] = {type l[L] = L =:= G}
but in practical uses it causes errors:
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
Type inference breaks here. How should I define type lambdas to make it work?
Upvotes: 14
Views: 702
Reputation: 5977
It seems that scala compiler could not handle naked types from type projections. I traced -Ytyper-debug
output and found that all needed type information is carried away but is rejected for no apparent reason. But it still possible to get type lambdas wrapping expressions inside a trait. That answer gave me an insight towards solution.
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]]
Upvotes: 0
Reputation: 4017
Consider this simplified version of your example:
trait Secret
type Curry = { type l[L] = Secret }
def foo[P[_], T](ev : P[T]) = ???
val bar: Curry#l[Int] = ???
foo(bar)
When calling foo
the value bar
is simply of type Secret
, the compiler doesn't know from where your particular Secret
comes from.
Your bar
value is just a Secret
, and it doesn't maintain information pointing back to Curry#l[Int]
.
The compiler cannot infer that P => Curry#l
and T => Int
.
The compiler only sees the Secret
and loses the Curry#l
context despite annotating the type with Curry#l[Int]
instead of Secret
.
Another example (coming from this question), exposing a similar behaviour:
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
situation is different, consider that CurryInt#l
, Curry#l
, and CurrStatic#l
are just type aliases. CurryObj.l
, instead, is an actual type, part of the concrete object CurryObj
.
Let's have a look to this (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 = S$@2c010477
scala> val foo1: CurryObj.l[Int] = S
foo1: CurryObj.l[Int] = S$@2c010477
Note that the type alias Curry#l[Int]
-> Secret
is resolved immediately, instead the actual type CurryObj.l[Int]
is kept.
Upvotes: 3
Reputation: 2453
slightly more verbose, but compiles :) (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
Upvotes: 2