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]).
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
// Start writing your ScalaFiddle code here
sealed trait DSL[A]{
// def run(): A ={
// this match {
// case GetLength(something) =>
// something.length
// case ShowResult(number) =>
// s"the length is $number"
// }
// }
}
case class GetLength(something: String) extends DSL[Int]
case class ShowResult(number: Int) extends DSL[String]
def run[A](fa:DSL[A]): A ={
fa match {
case GetLength(something) =>
something.length
case ShowResult(number) =>
s"the length is $number"
}
}
val dslGetLength = GetLength("123456789")
val length = run(dslGetLength)
val dslShowResult = ShowResult(length)
println(run(dslShowResult))
// print: the length is 9
scalafiddle here
why does the run function not compile in the DSL[A] trait, but worked outside?
how does type inference work in this case?
This is a case of generalized abstract data type.
When you have a DSL[A] and function returning A, compiler can prove that:
for case GetLength A=Int so you can return Int there
for case ShowResult A=String so you can return String
however, Scala 2 is known to not have a perfect support of GADTs, so sometimes compiler fails, even if it should work. I guess some compiler dev could figure out the exact case, but, interestingly, it can be worked around with:
sealed trait DSL[A]{
def run(): A = DSL.run(this)
}
object DSL {
def run[A](fa:DSL[A]): A ={
fa match {
case GetLength(something) =>
something.length
case ShowResult(number) =>
s"the length is $number"
}
}
}
case class GetLength(something: String) extends DSL[Int]
case class ShowResult(number: Int) extends DSL[String]
My wild guess would be that pattern matching in a generic method is kind of a special case in a compiler, which is not triggered when A is fixed. I think that, because the following code also works:
sealed trait DSL[A]{
def run(): A = runMe(this)
private def runMe[B](dsl: DSL[B]): B = {
dsl match {
case GetLength(something) =>
something.length
case ShowResult(number) =>
s"the length is $number"
}
}
}
whereas this fails as well:
sealed trait DSL[A]{
def run(): A = {
val fa: DSL[A] = this // make sure error is not related to special treatment of "this", this.type, etc
fa match {
case GetLength(something) =>
something.length
case ShowResult(number) =>
s"the length is $number"
}
}
}
cmd4.sc:5: constructor cannot be instantiated to expected type;
found : ammonite.$sess.cmd4.GetLength
required: ammonite.$sess.cmd4.DSL[A]
case GetLength(something) =>
^
cmd4.sc:7: constructor cannot be instantiated to expected type;
found : ammonite.$sess.cmd4.ShowResult
required: ammonite.$sess.cmd4.DSL[A]
case ShowResult(number) =>
^
Compilation Failed
In other words, I suspect that type parameter change how things are being evaluated:
def runMe[B](dsl: DSL[B]): B has a type parameter, so results of each case inside match are compared against B where for each case value of B can be proven to be some specific type (Int, String)
in def run: A however compiler is somehow prevented from making such analysis - IMHO it is a bug, but perhaps it is a result of some obscure feature.
From what I see the same error occurs in Dotty, so it is either duplicated bug or a limitation of a type-level checker (after all GADT aren't widely use din Scala, yet) - I would suggest reporting issue to Scala/Dotty team and letting them decide what it is.
Pattern matching seems to work differently depending on if type parameter comes from the enclosing method versus enclosing class. Here is a simplified example where using class type parameter A
trait Base[T]
case class Derived(v: Int) extends Base[Int]
class Test[A] {
def method(arg: Base[A]) = {
arg match {
case Derived(_) => 42
}
}
}
raises error
Error:(7, 12) constructor cannot be instantiated to expected type;
found : A$A87.this.Derived
required: A$A87.this.Base[A]
case Derived(_) => 42
^
whilst it works using method type parameter A
class Test {
def method[A](arg: Base[A]) = {
arg match {
case Derived(_) => 42
}
}
}
SLS 8.4: Pattern Matching Expressions seems to explain what happens in the method scenario
Let π be the type of the selector expression π and let π1,β¦,ππ be
the type parameters of all methods enclosing the pattern matching
expression. For every ππ, let πΏπ be its lower bound and ππ be
its higher bound. Every pattern πβπ1,,β¦,ππ can be typed in two
ways. First, it is attempted to type π with π as its expected type.
If this fails, π is instead typed with a modified expected type πβ²
which results from π by replacing every occurrence of a type
parameter ππ by undefined.
AFAIU, we have
e = arg
a1 = A
T = Base[A]
p1 = Derived(_)
First, it attempts to type π with π as its expected type, however Derived does not conform to Base[A]. Thus it attempts the second rule
If this fails, π is instead typed with a modified expected type πβ²
which results from π by replacing every occurrence of a type
parameter ππ by undefined.
Assuming undefined means something like existential type, then we have T' = Base[_], and because the following indeed holds
implicitly[Derived <:< Base[_]]
then pattern matching in the case of method type parameter becomes something like
class Test {
def method[A](arg: Base[A]) = {
(arg: Base[_]) match {
case Derived(_) => 42
}
}
}
which indeed compiles. This seems to be confirmed by making the class type parameter case successfully compile like so
class Test[A] {
def method(arg: Base[A]) = {
(arg: Base[_]) match {
case Derived(_) => 42
}
}
}
Therefore it seems the second rule is not attempted in type parameter inference for constructor patterns when type parameter comes from enclosing class.
At least these seems to be some of the moving pieces which hopefully someone with actual knowledge can assemble into coherent explanation, as I am mostly guessing.
run returns a generic A, but this only works in the case you didn't comment because you're accepting A as a type parameter (which the compiler can figure out).
The following would work instead:
sealed trait DSL[A] {
def run(): A
}
case class GetLength(something: String) extends DSL[Int] {
def run(): Int = something.length
}
case class ShowResult(number: Int) extends DSL[String] {
def run(): String = s"the length is $number"
}
val dslGetLength = GetLength("123456789")
val length = dslGetLength.run()
val dslShowResult = ShowResult(length)
println(dslShowResult.run())
You can play around with this code here on Scastie or alternatively on Scala Fiddle.
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.
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.