How is this possible:
import scala.util.{Try, Success}
import reflect._
case class Foo[A](x: A) extends Dynamic {
def get[T: ClassTag]: Option[T] = Try(x.asInstanceOf[T]) match {
case Success(r) => Some(r)
case _ => None
}
}
object Foo extends App {
val test = Foo("hi")
val wtf: Option[Int] = test.get[Int]
assert(wtf.isInstanceOf[Option[String]])
assert(wtf == Some("hi")) // how????
// val wtf2: Option[String] = wtf // does not compile even if above assert passes!!
}
Inspired by this question: Scala check type of generics
Due to type erasure, wtf.isInstanceOf[Option[String]] can only check that wtf is an instance of Option, but not the type parameter. Similarly, asInstanceOf[T] is actually a cast to Object at the runtime, and so it succeeds. You need to do
classTag[T].runtimeClass.cast(x)
instead.
The compiler can't use the information from asserts passing (you can imagine a compiler which could, but Scala simply isn't designed like that). It only knows that the type of wtf is Option[Int], so of course you can't initialize an Option[String] with it. If you want to get something like that, you need
wtf match {
case wtf2: Option[String] => ...
}
Of course, this doesn't work correctly due to point 1.
Related
What is the best way to make implicit resolution in scala work with singleton objects? This is especially common with Either and custom error objects.
In the code example below a method returns an application-specific error wrapped in IO. The error is represented by a singleton object extending Throwable. This code does not compile because scala is looking for an implicit for AppSpecificError.type instead of Throwable.
It is possible to put everything into variables with a specified type but it looks weird. This seems like a pretty common case, what is the best way to address it?
import cats.data.EitherT
import cats.effect.IO
import cats.implicits._
import scala.util.Random
object EitherTest {
case object AppSpecificError extends Throwable
def random: IO[Boolean] = {
IO(Random.nextBoolean())
}
def appMethod(flag: Boolean): EitherT[IO, Throwable, Int] = {
for {
flag <- EitherT.right(random)
result <- if (flag) {
EitherT.left[Int](AppSpecificError.pure[IO]) // compilation error here
} else {
EitherT.right[Throwable](10.pure[IO])
}
// can be more computations here
} yield result
}
def main(args: Array[String]): Unit = {
appMethod(true).value.unsafeRunSync() match {
case Right(s) => println("Success")
case Left(error) => println(error)
}
}
}
Error:(18, 14) type mismatch;
found : cats.data.EitherT[cats.effect.IO,_1,Int] where type _1 >: EitherTest.AppSpecificError.type <: Throwable
required: cats.data.EitherT[cats.effect.IO,Throwable,Int]
Note: _1 <: Throwable, but class EitherT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
result <- if (flag) {
Try to specify type parameters explicitly
EitherT.left[Int][IO, Throwable](AppSpecificError.pure[IO])
or use type ascription
EitherT.left[Int]((AppSpecificError: Throwable).pure[IO])
I'm trying to understand free monads. So with help of tutorials I wrote toy example to play with and now I don't understand why does it compile. Here it is:
import cats.free.Free
import cats.instances.all._
import cats.~>
trait Operation[+A]
case class Print(s: String) extends Operation[Unit]
case class Read() extends Operation[String]
object Console {
def print(s: String): Free[Operation, Unit] = Free.liftF(Print(s))
def read: Free[Operation, String] = Free.liftF(Read())
}
object Interpreter extends (Operation ~> Option) {
// why does this compile?
override def apply[A](fa: Operation[A]): Option[A] = fa match {
case Print(s) => Some(println(s))
case Read() => Some(readLine())
}
}
object Main {
def main(args: Array[String]) {
val program = for {
_ <- Console.print("What is your name?")
name <- Console.read
_ <- Console.print(s"Nice to meet you $name")
} yield ()
program.foldMap(Interpreter)
}
}
I'm talking about apply method of Interpreter. It should return Option[A], but I can return Option[Unit] and Option[String] here so I assume it should be a compilation error. But it's not. This code compiles and works(although Idea tells me that it's an error). Why is that?
UPD: but why doesn't this compile?
def test[A](o: Operation[A]): Option[A] = o match {
case Print(s) => Some(s)
case Read() => Some(Unit)
}
Your apply method is supposed to return Option[A] where A is determined by the type of the argument. That is if the argument has type Operation[Unit], the result should also be an Option[Unit] and so on.
Now your body adheres to that contract perfectly. Yes, you do have a case where you return an Option[Unit] instead of a general Option[A], but you only do that if the argument was an instance of Print and thus an Operation[Unit]. That is you only ever return an Option[Unit] when the argument was an Operation[Unit], so the contract is not broken. The same is true with Read and String. Note that if you returned an Option[Unit] in the case for Read, that'd be an error because you'd now be returning a type other than that of the argument.
So that's why the code is semantically correct, but why does it compile? That's because the Scala type checker (unlike IntelliJ's approximation thereof) is smart enough to take the additional type information into account when pattern matching. That is, in the case Print it knows that you've just matched a value of type Operation[A] against a pattern of type Operation[Unit], so it assigns A = Unit inside the case's body.
Regarding your update:
case Print(s) => Some(s)
Here we have a pattern of type Operation[Unit] (remember that Print extends Operation[Unit]), so we should get a result of type Option[Unit], but Some(s) has type Option[String]. So that's a type mismatch.
case Read() => Some(Unit)
First of all Unit it the companion object of the Unit type, so it has its own type, not type Unit. The only value of type Unit is ().
Aside from that, it's the same situation as above: The pattern has type Operation[String], so the result should be Operation[String], not Operation[Unit] (or Operation[Unit.type]).
Is there any elegant way, to arrive from:
def foo[T: TypeTag](a: A[T]) {
// can we match on the type of T here?
}
at a match expression on the type of T?
Obviously this below does not overcome erasure for T, so must we inspect the TypeTag by hand?
a match {
case _:A[SomeSpecificType] => ...
Or does scala afford some elegance to that end?
Sadly no, as the compiler does not take type tags into account if you add type checks to a pattern. I'm not sure why and whether this is planned. You can however compare type tags for equality:
typeOf[T] =:= typeOf[List[String]]
You can use that in an if or match condition and then cast to the target type.
After thinking a bit more about it, I recognized that it would be quite easy to write my own pattern extractor, that hides the check and cast:
import scala.reflect.runtime.universe._
class TypeTest[A: TypeTag]() {
def unapply[B: TypeTag](v: B): Option[A] =
if(typeOf[B] <:< typeOf[A])
Some(v.asInstanceOf[A])
else
None
}
object TypeTest {
def apply[A: TypeTag] = new TypeTest()
}
Now, we can do stuff like this:
def printIfStrings[T: TypeTag](v: T) {
val string = TypeTest[List[String]]
v match {
case string(s) => printString(s)
case _ =>
}
}
def printString(s: List[String]) {
println(s)
}
printIfStrings(List(123))
printIfStrings(List("asd"))
This is already quite neat, but as Scala does not support passing an argument directly to an extractor in a pattern, we have to define all extractors as val string before the match expression.
Macros
Macros can transform code, so it should be easy enough to transform any unchecked typechecks in a match expression into an appropriate pattern or add explicit checks using the type tags directly.
This however requires that we have a macro invocation wrapped around every critical match expression, which would be quite ugly. An alternative is to replace match expressions by some method call that takes a partial function as an argument. This method can be provide for an arbitrary type using an implicit conversion.
The only remaining problem then is that the compiler typechecks the code before any macros are invoked, so it will generate a warning for the unchecked cast even though it is now checked. We can still us #unchecked to suppress these warnings.
I chose to replace type checks in patterns by the extractor described above instead of adding a condition to the case and explicit type casts. The reason for that is that this transformation is local (I just have to replace a subexpression with another).
So here is the macro:
import scala.language.experimental.macros
import scala.language.implicitConversions
import scala.reflect.macros.blackbox.Context
object Switch {
implicit class Conversion[A](val value: A) {
def switch[B](f: PartialFunction[A, B]): B = macro switchImpl
}
def switchImpl(c: Context)(f: c.Tree): c.Tree = {
import c.universe._
val types = collection.mutable.Map[Tree,String]()
val t1 = new Transformer {
override def transformCaseDefs(trees: List[CaseDef]) = {
val t2 = new Transformer {
override def transform(tree: Tree) = {
def pattern(v: String, t: Tree) = {
val check = types.getOrElseUpdate(t, c.freshName())
pq"${TermName(check)}(${TermName(v)})"
}
tree match {
case Bind(TermName(v),Typed(Ident(termNames.WILDCARD),
Annotated(Apply(
Select(New(Ident(TypeName("unchecked"))),
termNames.CONSTRUCTOR), List()
), t)))
=> pattern(v,t)
case Bind(TermName(v),Typed(Ident(termNames.WILDCARD),t))
=> pattern(v,t)
case _ => super.transform(tree)
}
}
}
t2.transformCaseDefs(trees)
}
}
val tree = t1.transform(c.untypecheck(f))
val checks =
for ((t,n) <- types.toList) yield
q"val ${TermName(n)} = Switch.TypeTest[$t]"
q"""
..$checks
$tree(${c.prefix}.value)
"""
}
import scala.reflect.runtime.universe._
class TypeTest[A: TypeTag]() {
def unapply[B: TypeTag](v: B): Option[A] =
if(typeOf[B] <:< typeOf[A]) Some(v.asInstanceOf[A])
else None
}
object TypeTest {
def apply[A: TypeTag] = new TypeTest()
}
}
And now magically type checks in patterns work:
import Switch.Conversion
val l = List("qwe")
def printIfStrings2[T: scala.reflect.runtime.universe.TypeTag](v: T) {
v switch {
case s: Int => println("int")
case s: List[String] #unchecked => printString(s)
case _ => println("none")
}
}
printIfStrings2(l)
printIfStrings2(List(1, 2, 3))
printIfStrings2(1)
I'm not sure whether I handle all possible cases correctly, but every thing I tried worked fine. A type with multiple annotations is possibly not handled correctly if it is also annotated by #unchecked, but I couldn't find an example in the standard library to test this.
If you leave out the #unchecked the result is exactly the same, but as mentioned above you will get a compiler warning. I don't see a way to get rid of that warning with normal macros. Maybe annotation macros can do it but they are not in the standard branch of Scala.
consider a generic function:
def genericFn[T](fn: T => Boolean): Unit = {
// do something involves T
}
is it possibile to restrict T (at compile time) to be a simple type, not a type like List[Int]?
the underling problem I want to solve is something like this:
var actorReceive: Receive = PartialFunction.empty
def addCase[T](handler: T => Boolean): Unit = {
actorReceive = actorReceive orElse ({
case msg: T => // call handle at some point, plus some other logic
handler(msg)
})
}
the addCase function would result in type erasure warning, which could be solved by requiring ClassTag like: def addCase[T: ClassTag](..., but ClassTag still can't guard against calls like:
addCase[List[Int]](_ => {println("Int"); true})
addCase[List[String]](_ => {println("String"); false})
actorReceive(List("str")) // will print "Int"
the above code will print "Int" while not issuing any warning or error at all, is there any way out?
There is no way to enforce this in the type system as-is, without reflection.
The nicest way to do this would be to have a type-class such as NonEraseable[A], that provides evidence that a type has no type parameters that would be erased at runtime. An implicit NonEraseable[A] in scope should mean that A has no type parameters. Seeing as these would be tedious to manually create, an implicit macro can do the job:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
trait NonEraseable[A]
object NonEraseable {
implicit def ev[A]: NonEraseable[A] = macro evImpl[A]
def evImpl[A](c: Context)(implicit tt: c.WeakTypeTag[A]): c.Expr[NonEraseable[A]] = {
import c.universe._
val tpe = weakTypeOf[A]
if(tpe.dealias.typeArgs.isEmpty)
c.Expr[NonEraseable[A]](q"new NonEraseable[$tpe] {}")
else
c.abort(c.enclosingPosition, s"$tpe contains parameters that will be erased at runtime.")
}
}
Use case:
def onlySimple[A : NonEraseable](value: A): Unit = println(value)
scala> onlySimple(1)
1
scala> onlySimple(List(1, 2, 3))
<console>:13: error: List[Int] contains parameters that will be erased at runtime.
onlySimple(List(1, 2, 3))
^
Using this, you can enforce at compile time that a type parameter A with a context bound NonEraseable is the kind of type you want. (Assuming you don't cheat and manually create instance of the type class)
You can at least get it to fail at run-time as follows:
def addCase[T: ClassTag](handler: T => Boolean): Unit =
if (classTag[T].runtimeClass.getTypeParameters.nonEmpty) {
// throw an exception
} else {
// the main code
}
Compile-time failure can be achieved using a macro instead of a function (approximate, untested):
def addCase[T](handler: T => Boolean): Unit = macro addCaseImpl
def addCaseImpl[T: c.WeakTypeTag](c: Context)(handler: c.Expr[T => Boolean]): c.Expr[Unit] =
if (c.weakTypeOf[T].typeParams.nonEmpty) {
c.abort(c.enclosingPosition, "Generic types not allowed in addCase")
} else {
// generate code for main line
}
(this is based on the article at http://bertails.org/2015/02/15/abstract-algebraic-data-type)
First, I am defining an abstract version of scala.Option.
import scala.language.higherKinds
trait OptionSig {
type Option[+_]
type Some[+A] <: Option[A]
type None <: Option[Nothing]
}
abstract class OptionOps[Sig <: OptionSig] extends Extractors[Sig] {
def some[A](x: A): Sig#Some[A]
def none: Sig#None
def fold[A, B](opt: Sig#Option[A])(ifNone: => B, ifSome: A => B): B
}
I want to be able to use pattern matching on Sig#Option[A] so Extractors looks like that:
trait Extractors[Sig <: OptionSig] { self: OptionOps[Sig] =>
object Some {
def unapply[A](opt: Sig#Option[A]): scala.Option[A] =
fold(opt)(scala.None, a => scala.Some(a))
}
object None {
def unapply[A](opt: Sig#Option[A]): Option[Unit] =
fold(opt)(scala.Some(()), _ => scala.None)
}
}
Now I can write this program:
class Program[Sig <: OptionSig](implicit ops: OptionOps[Sig]) extends App {
import ops._
val opt: Sig#Option[Int] = some(42)
opt match {
case None(_) => sys.error("")
case Some(42) => println("yay")
case Some(_) => sys.error("")
}
}
And I can test it with this implementation.
trait ScalaOption extends OptionSig {
type Option[+A] = scala.Option[A]
type Some[+A] = scala.Some[A]
type None = scala.None.type
}
object ScalaOption {
implicit object ops extends OptionOps[ScalaOption] {
def some[A](x: A): ScalaOption#Some[A] = scala.Some(x)
val none: ScalaOption#None = scala.None
def fold[A, B](opt: ScalaOption#Option[A])(ifNone: => B, ifSome: A => B): B =
opt match {
case scala.None => ifNone
case scala.Some(x) => ifSome(x)
}
}
}
object Main extends Program[ScalaOption]
It looks like it works but there is one annoying thing I cannot figure out.
With, scala.Option, the type of s in Option(42) match { case s # Some(42) => s } is Some[Int]. But with my snippet above, it is Sig#Option[Int] and I would like to make it Sig#Some[Int] instead.
So I tried the following to be closer to what scalac generates for its case classes:
trait Extractors[Sig <: OptionSig] { self: OptionOps[Sig] =>
object Some {
def unapply[A](s: Sig#Some[A]): scala.Option[A] =
fold(s)(scala.None, a => scala.Some(a))
}
object None {
def unapply(n: Sig#None): Option[Unit] =
fold(n)(scala.Some(()), (_: Any) => scala.None)
}
}
But now I get warnings like the following:
[warn] Main.scala:78: abstract type pattern Sig#None is unchecked since it is eliminated by erasure
[warn] case None(_) => sys.error("")
I am not sure why this is happening as Sig#None is a subtype of Sig#Option[Int] and this is known at compile time.
Also the runtime is still ok, but the inferred type is still not the one I was expecting.
So the questions are
why is type erasure mentioned here despite the subtyping information?
how to get Sig#Option[Int] for s in (some(42): Sig#Option[Int]) match { case s # Some(42) => s }
Unfortunately, you cannot do what you want. The problem is that it is not enough that scalac know that Sig#None <: Sig#Option[A], it must be able to verify that the value it is passing to unapply is, in fact, Sig#None. This works for scala.Option, because the compiler can generate an instanceof check to verify that a type is actually a Some or a None before passing it to the unapply method. If it fails the check, it skips that pattern (and unapply is never called).
In your case, since scalac only knows that opt is a Sig#Option[Int], it cannot do anything to guarantee that the value is actually a Some or None before passing it along to unapply.
So what does it do? It passes the value along anyways! What does this mean? Well, let's modify your extractors a bit:
trait Extractors[Sig <: OptionSig] { self: OptionOps[Sig] =>
object Some {
def unapply[A](s: Sig#Some[A]): scala.Option[A] =
fold(s)(scala.None, a => scala.Some(a))
}
object None {
def unapply(n: Sig#None): Option[Unit] =
scala.Some(())
}
}
All we've done is stopped using fold in the None case. Since we know the argument must be a Sig#None, why even bother calling fold, right? I mean, we wouldn't expect a Sig#Some to be passed here, right?
When we run this example, you'll hit a RuntimeException, because our very first pattern match succeeds and calls ???. In the case of scala.Option, the pattern would fail because it's guarded by a generated instanceof check.
I've gisted another example that shows another danger, where we add a small constraint to Sig#Some, which let's us avoid the fold in the Some case too: https://gist.github.com/tixxit/ab99b741d3f5d2668b91
Anyways, your specific case is technically safe. We know that you used fold and so it is safe to use with an Sig#Option. The problem is that scalac doesn't know that.