I don't understand why thid fold doesn't compile. Could anyone give me a clue?
sealed trait ListG[A] {
def fold[A,B](end: B, f: (A,B) => B): B = this match {
case End() => end
case Cons(hd,tl) => f(hd, tl.fold(end,f))
}
}
Error:(20, 28) type mismatch;
found : hd.type (with underlying type A)
required: A
case Cons(hd,tl) => f(hd, tl.fold(end,f))
^
final case class EndA extends ListG[A]
final case class Cons[A](hd:A, tl:ListG[A]) extends ListG[A]
You're shadowing type parameter A of ListG when you define an additional type parameter A on the fold function.
Adding type ascription appears to fix the problem.
case Cons(hd:A, tl) => ...
^^
There is a warning about type erasure but it does compile and appears to run.
Related
This code compiles with an error:
def f1[T](e: T): T = e match {
case i:Int => i
case b:Boolean => b
}
// type mismatch;
// found : i.type (with underlying type Int)
// required: T
// case i:Int => i ...
And this code implementing GADT looks pretty identical from type checking perspective, but compiles without error:
sealed trait Expr[T]
case class IntExpr(i: Int) extends Expr[Int]
case class BoolExpr(b: Boolean) extends Expr[Boolean]
def eval[T](e: Expr[T]): T = e match {
case IntExpr(i) => i
case BoolExpr(b) => b
}
In both cases inside pattern matching expression we know that i and b are Int and Boolean. Why compilation failed on first example and succeeded on the second one?
The first case is unsound because you underestimate the variety of types in Scala type system. It would make sense if, when we took case i:Int branch we knew T was Int, or at least a supertype of Int. But it doesn't have to be! E.g. it could be 42.type or a tagged type.
There's no such problem in the second case, because from IntExpr <: Expr[T], the compiler does know T must be exactly Int.
You ask of your function to return a type T, then you pattern-match against Int and Boolean.
Except your function has no evidence that Int and Boolean are also of type T: when you pattern-match, you introduce the constraint that Int <: T and Boolean <: T.
You could either replace the return type T by a fixed type like String and return a String, or introduce a constraint that will satisfy both the case Int and Boolean.
//this compiles
def f1[T](e: T ): String = e match {
case _:Int => "integer"
case _:Boolean => "boolean"
}
//this compiles too, but will return AnyVal
def f1[T >: AnyVal](e: T ): T = e match {
case i:Int => i
case b:Boolean => b
}
Basically you can't just return any type T dynamically because you need to prove at compile time that your function type-checks out.
The other function in your example avoids the issue by encapsulating type constraints within case classes IntExpr <: Expr[Int] and BoolExpr <: Expr[Boolean] (notice how Expr[_] would be the equivalent of T in the constraints I mentioned above). At compile time, T is properly identified in all cases (e.g in the IntExpr you know it's an Int)
In addition to #Esardes answer, this worked by defining a type bound for T:
scala> def f1[T >: AnyVal](e: T):T = e match {
| case i:Int => i
| case b:Boolean => b
| }
f1: [T >: AnyVal](e: T)T
scala> f1(1)
res3: AnyVal = 1
scala> f1(true)
res4: AnyVal = true
I have:
sealed trait Par[A]{def foo = ???}
case class Unit[A](v: () => A) extends Par[A]
case class Map2[L, R, A](parLeft: Par[L],
parRight: Par[R],
map: (L, R) => A) extends Par[A]
my problem is that when I pattern match on p:Par[A] to do something like this:
def getSequentially: A = this match {
case Par.Unit(f) => f()
case Par.Map2(a, b, f) => f(a.getSequentially, b.getSequentially)
}
L and R are inferred to be Any in Intellij's type inspector and getSequentially calls are highlighted in red, warning: type mismatch, expected Nothing, actual Any, since f is expected to be of type: (Nothing, Nothing) => A. Although it actually runs and compiles.
I think I understand what the problem is and I should be able to solve it with existential types in the definition of Map2. Only problem is that the map parameter has a dependent type, so I don't know how to do that. Maybe I should do a variation of the AUX pattern?
My question is, firstly why it compiles and secondly, if there is a way of restructuring the type dependency so that it no longer issues a warning.
If you want to have existentials you can use typed patterns:
sealed trait Par[A]{
def getSequentially: A = this match {
case x: Par.Unit[A] => x.v()
case x: Par.Map2[_, _, A] => x.map(x.parLeft.getSequentially, x.parRight.getSequentially)
}
}
object Par {
case class Unit[A](v: () => A) extends Par[A]
case class Map2[L, R, A](parLeft: Par[L],
parRight: Par[R],
map: (L, R) => A) extends Par[A]
}
IntelliJ seems not to highlight this.
This example is taken from an exercise of "Essential Scala" from Underscore.io.
Following is definition of algebraic sum type Maybe[A] which mimics some basic features of Option[A] and a list of Maybe[Int].
sealed trait Maybe[A] {
def flatMap[B](fn: A => Maybe[B]): Maybe[B] =
this match {
case Full(v) => fn(v)
case Empty() => Empty[B]()
}
def map[B](fn: A => B): Maybe[B] =
this match {
case Full(v) => Full(fn(v))
case Empty() => Empty[B]()
}
}
final case class Full[A](value: A) extends Maybe[A]
final case class Empty[A]() extends Maybe[A]
val list = List(Full(3), Full(2), Full(1))
I tried to replace the elements in list with odd values with Empty[Int] therefore resulting List(Empty(), Full(2), Empty()) by this statement:
list.map(maybe => maybe flatMap { x => if(x % 2 == 0) Full(x) else Empty() })
and this was the exactly same with the answer from the book.
But I got an error:
Error:(41, 26) no type parameters for method flatMap: (fn: Int => A$A22.this.Maybe[B])A$A22.this.Maybe[B] exist so that it can be applied to arguments (Int => Product with Serializable with A$A22.this.Maybe[_ <: Int])
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Int => Product with Serializable with A$A22.this.Maybe[_ <: Int]
required: Int => A$A22.this.Maybe[?B]
list.map(maybe => maybe flatMap { x => if(x % 2 == 0) Full(x) else Empty() })
^
So I wrote type parameter then It worked well:
list.map(maybe => maybe flatMap[Int] { x => if(x % 2 == 0) Full(x) else Empty() })
I thought it would be ok even if I don't provide the type parameter Int because it seems that it was possible to infer the type parameter from the type of argument for flatMap x => if(x % 2 == 0) Full(x) else Empty(). What is wrong? I heard that Product and Serializable automatically mix-in into case class so that the type of the function literal above is Int => Product with Serializable with A$A22.this.Maybe[_ <: Int] as shown in the error message. Is this related to the issue?
The problem is Empty(): the compiler has no reason to pick any type parameter for it other than Nothing (in particular, it doesn't look at if's other branch). So it ends up with Full[Int] in one branch and Empty[Nothing] in another, and their common type is Product with Serializable with Maybe[_ <: Int], as shown in the error message. From this result type, obviously the type parameter for flatMap can't be inferred.
When defining a trait which should only be extended by case classes, it's normal to include trait Maybe[A] extends Product with Serializable so they'll be subsumed in such upper bound calculation, but you'll still end up with Maybe[_ <: Int] (in case you are unfamiliar with this notation, it means "Maybe whose type parameter is some subtype of Int"): the compiler notices that Full[Int] is a subtype of Maybe[Int] and Empty[Nothing] is a subtype of Maybe[Nothing], but Maybe[Nothing] is not a subtype of Maybe[Int].
I expect you'll see the better way to fix the problem in following exercises, but for now the fix is to specify Empty[Int]().
I ran into a puzzling type inference problem with case classes. Here's a minimal example:
trait T[X]
case class Thing[A, B, X](a: A, f: A => B) extends T[X]
def hmm[X](t: T[X]) = t match {
case Thing(a, f) => f("this really shouldn't typecheck")
}
Scala decides that a: Any and f: Any => Any, but that's inappropriate; they really ought to have types a: SomeTypeA and f: SomeTypeA => SomeTypeB, where SomeTypeA and SomeTypeB are unknown types.
Another way of saying this is that I think the hypothetical Thing.unapply method should look something like
def unapply[X](t: T[X]): Option[(A, A => B)] forSome { type A; type B } = {
t match {
case thing: Thing[_, _, X] => Some((thing.a, thing.f))
}
}
This version correctly gives a type error at f("this really shouldn't typecheck").
Does this seem like a bug in the compiler, or am I missing something?
Edit: This is on Scala 2.10.3.
Mark Harrah pointed this out in the #scala channel on Freenode: yes, this is a bug.
https://issues.scala-lang.org/browse/SI-6680
Could someone explain why the following code compiles?
Option("foo") match {
case x: List[String] => println("A")
case _ => println("B")
}
This gives me an (expected) warning about type erasure, but it still compiles. I expected this to throw a type error, like it does if I matched on "foo" instead of Option("foo").
Thanks!
The code is commented, so let's take a moment to savor that:
/** If we can absolutely rule out a match we can fail early.
* This is the case if the scrutinee has no unresolved type arguments
* and is a "final type", meaning final + invariant in all type parameters.
*/
Notice that None is not final, for instance. I know, right?
If you ever try scalac -Ypatmat-debug, the comment here might help:
https://github.com/scala/scala/pull/650
Reachability is almost within reach:
https://issues.scala-lang.org/browse/SI-6146
But I don't see any promises about what might someday be warnable. For performance reasons? One could also say, why should it warn about an instanceOf[Foo[_]]?
For now, the spec sections 8.2 - 8.4 motivate why matching against Foo[a] is interesting (because of the bounds a acquires). I think I'll go read that again. After some coffee.
trait Foo[+A]
final class Fuzz[+A] extends Foo[A]
final object Fooz extends Foo[Nothing]
object Futz extends Foo[Nothing]
//error
Fooz match {
case x: List[_] => println("A")
case _ => println("B")
}
//no error
Futz match { ... }
I would assume that the compiler is treating both Option and List as Product, which is why it compiles. As you say, the warning about type erasure is expected. Here's an example that uses another Product:
scala> Option("foo") match {
| case x: Tuple2[String,String] => println("TUPLE")
| case x: List[String] => println("LIST")
| case _ => println("OTHER")
| }
<console>:9: warning: non variable type-argument String in type pattern (String, String) is unchecked since it is eliminated by erasure
case x: Tuple2[String,String] => println("TUPLE")
^
<console>:10: warning: non variable type-argument String in type pattern List[String] is unchecked since it is eliminated by erasure
case x: List[String] => println("LIST")
^
UPDATE w/r/t case classes (because of the comment below):
scala> case class Foo(bar: Int)
defined class Foo
scala> val y: Product = Foo(123)
y: Product = Foo(123)
I noticed that an error is shown when the class of the value you match is declared as final (and we know that String is final). I still don't know why there's no error without it.