Difference between home made extractor and case class extractor - scala

According to the scala specification, the extractor built by case classes is the following (scala specification ยง5.3.2):
def unapply[tps](x: c[tps]) =
if (x eq null) scala.None
else scala.Some(x.xs11, ..., x.xs1k)
For implementation reasons, I want to be able to mimic the behavior of this extractor on a non-case class.
However, my implementation fails to reproduce the same behavior.
Here is an example of the difference i have:
trait A
sealed trait B[X <: A]{ val x: X }
case class C[X <: A](x: X) extends B[X]
class D[X <: A](val x: X) extends B[X]
object D {
def unapply[X <: A](d: D[X]): Option[X] =
if (d eq None) None
else Some(d.x)
}
def ext[X <: A](b: B[X]) = b match {
case C(x) => Some(x)
case D(x) => Some(x)
case _ => None
}
I have the following warning :
<console>:37: warning: non variable type-argument X in type pattern D[X] is unchecked since it is eliminated by erasure
case D(x) => Some(x)
Notice the warning occurs only in the D case, not in the case-class textractor case.
Do you have any idea about the cause of the warning / about what I should do to avoid this warning ?
Note: If you want to test it in REPL, the easiest way is:
To activate unchecked warning
scala> :power
scala> settings.unchecked.value = true
To copy above code in paste mode:
scala> :paste
[copy/paste]
[ctrl + D]
Edit: As Antoras mentioned it should be a compiler bug, maybe the scala version could be useful: scala 2.9.0.1
(after a quick test, still there in scala 2.9.1RC2)

This seems to be a compiler bug. I have analyzed the output of the compiler AST (with fsc -Xprint:typer <name_of_file>.scala). It interprets both as the same:
...
final <synthetic> object C extends java.lang.Object with ScalaObject with Serializable {
def this(): object test.Test.C = {
C.super.this();
()
};
final override def toString(): java.lang.String = "C";
case <synthetic> def unapply[X >: Nothing <: test.Test.A](x$0: test.Test.C[X]): Option[X] = if (x$0.==(null))
scala.this.None
else
scala.Some.apply[X](x$0.x);
case <synthetic> def apply[X >: Nothing <: test.Test.A](x: X): test.Test.C[X] = new test.Test.C[X](x);
protected def readResolve(): java.lang.Object = Test.this.C
};
...
final object D extends java.lang.Object with ScalaObject {
def this(): object test.Test.D = {
D.super.this();
()
};
def unapply[X >: Nothing <: test.Test.A](d: test.Test.D[X]): Option[X] = if (d.eq(null))
scala.None
else
scala.Some.apply[X](d.x)
};
...
The method signature of both methods unapply are identical.
Furthermore the code works fine (as expected due to identical methods):
trait A {
def m = "hello"
}
class AA extends A
sealed trait B[X <: A]{ val x: X }
case class C[X <: A](x: X) extends B[X]
class D[X <: A](val x: X) extends B[X]
object D {
def apply[X <: A](x: X) = new D(x)
def unapply[X <: A](d: D[X]): Option[X] =
if (d eq null) None
else Some(d.x)
}
def ext[X <: A](b: B[X]) = b match {
case C(x) => Some("c:"+x.m)
case D(x) => Some("d:"+x.m)
case _ => None
}
println(ext(C[AA](new AA())))
println(ext(D[AA](new AA())))

Related

how to call filter on both Seq an Vector

I'd like to make a method that take a vector or a seq of of some type, let's say Int, and calls filter on it.
For example:
implicit class SharedOps[F](xs: F)(implicit ev: OneOf[F, Seq[Int] ::: Vector[Int] ::: HSNil]) {
def filter(x: Int):F = xs.filter({a:Int => a == x})
}
OneOf basically checks that F is either a Seq[Int] or a Vector[Int]
The tricky part is that I want the filter to return the same type as the input (e.g. Seq[Int] or Vector[Int]) but compiler is complaining
error: type mismatch;
found : scala.this.Function1[scala.this.Int,scala.this.Boolean]
required: scala.this.Int
def filter(x: Int):F = xs.filter({a:Int => a == x})
Somehow compiler forgot I started with a collection of sorts and thinks xs is a single thing.
So I changed the design:
implicit class SharedOps2[A,F[A]<:TraversableLike[A,A]](xs: F[A])(implicit ev: OneOf[F[A], Seq[Int] ::: Vector[Int] ::: HSNil]) {
def filter(x: A): F[A] = xs.filter({ a: Int => a == x })
}
Now compiler is complaining that: Expression of type A doesn't conform to expected type F[A]
Not sure how to take it from here. I'd like to avoid shapeless coproducts at this point.
For completeness here's the OneOf code:
sealed trait HSubset // HList
#implicitNotFound("No member of type class HSubset in scope for ${H}")
trait :::[+H, +T <: HSubset] extends HSubset // HCons ::
sealed trait HSNil extends HSubset // HNil
#implicitNotFound("No member of type class BelongsTo in scope for ${T}")
trait BelongsTo[T, U <: HSubset]
object BelongsTo {
implicit def baseCase[H, U <: HSubset]: BelongsTo[H, H ::: U] = null
implicit def recursiveCase[H, U <: HSubset, T](implicit ev: T BelongsTo U): T BelongsTo (H ::: U) = null
}
#implicitNotFound("No member of type class SubsetOf in scope for ${U} ${T}")
trait SubsetOf[U <: HSubset, T <: HSubset]
object SubsetOf {
implicit def baseCase[U1, U <: HSubset](implicit s: U1 BelongsTo U): SubsetOf[U1 ::: HSNil, U] = null
implicit def recursiveCase[U <: HSubset, T1, T <: HSubset](implicit ev1: T1 BelongsTo U, ev2: T SubsetOf U): (T1 ::: T) SubsetOf U = null
}
trait OneOf[T, U <: HSubset]
object OneOf {
implicit def baseCase[U <: HSubset, T](implicit s: T BelongsTo U): T OneOf U = null
implicit def recursiveCase[T, Ev <: HSubset, Target <: HSubset](implicit ev1: T OneOf Ev, ev2: Ev SubsetOf Target): T OneOf Target = null
}
This is the proposed typeclas.
My advice would be to use specific types like List & Vector instead of Seq.
trait Filter[F[_]] {
def filter[A](fa: F[A])(p: A => Boolean): F[A]
}
object Filter {
implicit final val VectorFilter: Filter[Vector] =
new Filter[Vector] {
override final def filter[A](vector: Vector[A])(p: A => Boolean): Vector[A] =
vector.filter(p)
}
implicit final val SeqFilter: Filter[Seq] =
new Filter[Seq] {
override final def filter[A](seq: Seq[A])(p: A => Boolean): Seq[A] =
seq.filter(p)
}
}
object syntax {
object filter {
implicit class FilterOps[F[_], A](private val fa: F[A]) extends AnyVal {
#inline
final def filter(p: A => Boolean)(implicit ev: Filter[F]): F[A] =
ev.filter(fa)(p)
}
}
}
import syntax.filter._
def foo[F[_] : Filter](xs: F[Int]): F[Int] =
xs.filter(i => (i % 2) == 0)
Which you can use like:
foo(Vector(1, 2, 3))
// res: Vector[Int] = Vector(2)
foo(List(1, 2, 3))
// could not find implicit value for evidence parameter of type ammonite.$sess.cmd0.Filter[List]
foo(Seq(1, 2, 3))
// res: Seq[Int] = List(2)
BTW, it is worth mentioning that such typeclass already exists in cats
TraversableLike.filter returns Repr, which is type passed as second type parameter to TraversableLike. You need F[A] there instead of A. This compiles for me:
implicit class SharedOps2[A,F[A]<:TraversableLike[A,F[A]]](xs: F[A])(implicit ev: OneOf[F[A], Seq[A] ::: Vector[A] ::: HSNil]) {
def filter(x: A): F[A] = xs.filter({ a: A => a == x })
}
Note also type of a changed to A, because this is the type inside F[A] collection.
fiddle

Generic sum of two numeric expressions

I am writing a little toy language built on top of expressions. Here is some code to get the idea:
trait Expression[+T] {
def eval: T
}
case class Literal[+T](value: T) extends Expression[T] {
def eval = value
}
The parser builds a tree of expressions which are then evaluated by calling the eval method. Now I want to add a Sum expression that represents the sum of two other expressions:
case class Sum[+T: Numeric](left: Expression[T], right: Expression[T]) {
def eval = implicitly[Numeric[T]].plus(left.eval, right.eval)
}
This works fine if the left and right expression have the same type (as specified by the constructor). But naturally I would like it to work in the following case as well:
Sum(Literal(1.1), Literal(1))
This does not work because the compiler does not find an implicit argument of type Numeric[AnyVal], which makes sense.
I came up with the following code, using type bounds, to try to fix the issue:
case class Sum2[+T: Numeric, L <% T, R <% T](left: Expression[L], right: Expression[R]) extends Expression[T] {
def eval = implicitly[Numeric[T]].plus(left.eval, right.eval)
}
Now the compiler complains that left.eval and right.eval are not of type T. Casting to T using asInstanceOf[T] generates more compiler errors because of ambiguous implicit arguments.
What is the proper way to achieve this?
As it was pointed in the comments, the fact that there is safe conversion from Int to Double for your operation is not enough for the compiler to be able prove that this conversion is valid in all relevant contexts. I'm not aware of any simpler way to achieve what you want than this code (see also online):
trait Expression[+T] {
def eval: T
}
trait TypeConverter[S, T] {
def convert(value: S): T
}
trait TypeConverterLowPriority {
implicit def compose[A, B, C](implicit aToB: TypeConverter[A, B], bToC: TypeConverter[B, C]): TypeConverter[A, C] = new TypeConverter.TypeConverterImpl(a => bToC.convert(aToB.convert(a)))
}
object TypeConverter extends TypeConverterLowPriority {
class TypeConverterImpl[S, T](f: S => T) extends TypeConverter[S, T] {
override def convert(value: S): T = f(value)
}
def sameType[T]: TypeConverter[T, T] = new TypeConverterImpl(identity)
implicit val intToDouble: TypeConverter[Int, Double] = new TypeConverterImpl(_.toDouble)
implicit val shortToInt: TypeConverter[Short, Int] = new TypeConverterImpl(_.toInt)
// add more "primitive" type conversions here
}
case class Literal[+T](value: T) extends Expression[T] {
def eval = value
}
trait BinaryOpImpl[A, B, R] {
protected val numericR: Numeric[R]
protected val aToR: TypeConverter[A, R]
protected val bToR: TypeConverter[B, R]
final def eval(left: A, right: B): R = evalImpl(aToR.convert(left), bToR.convert(right))
protected def evalImpl(left: R, right: R): R
}
trait BinaryOpImplCompanionLowPriority[Ops[_, _, _]] {
protected def build[A, B, R](numericR: Numeric[R], aToR: TypeConverter[A, R], bToR: TypeConverter[B, R]): Ops[A, B, R]
implicit def castLeftToRight[L, R: Numeric](implicit tcl: TypeConverter[L, R]): Ops[L, R, R] = build(implicitly[Numeric[R]], tcl, TypeConverter.sameType)
implicit def castRightToLeft[L: Numeric, R](implicit tcr: TypeConverter[R, L]): Ops[L, R, L] = build(implicitly[Numeric[L]], TypeConverter.sameType, tcr)
}
trait BinaryOpImplCompanion[Ops[_, _, _]] extends BinaryOpImplCompanionLowPriority[Ops] {
implicit def sameType[T: Numeric]: Ops[T, T, T] = build(implicitly[Numeric[T]], TypeConverter.sameType, TypeConverter.sameType)
}
class SumImpl[A, B, R](val numericR: Numeric[R], val aToR: TypeConverter[A, R], val bToR: TypeConverter[B, R]) extends BinaryOpImpl[A, B, R] {
override protected def evalImpl(left: R, right: R): R = numericR.plus(left, right)
}
object SumImpl extends BinaryOpImplCompanion[SumImpl] {
override protected def build[A, B, R](numericR: Numeric[R], aToR: TypeConverter[A, R], bToR: TypeConverter[B, R]): SumImpl[A, B, R] = new SumImpl(numericR, aToR, bToR)
}
case class Sum[+T, L, R](left: Expression[L], right: Expression[R])(implicit impl: SumImpl[L, R, T]) extends Expression[T] {
def eval = impl.eval(left.eval, right.eval)
}
usage example:
def test(): Unit = {
println(Sum(Literal(3), Literal(1)).eval)
println(Sum(Literal(1.1), Literal(1)).eval)
println(Sum(Literal(1), Literal(1.1)).eval)
println(Sum(Literal[Short](1), Literal(1.12)).eval) // composite conversion Short -> Int -> Double
}
Essentially the idea is to have one implicit variable that encapsulates all 3 relevant types instead of having 3 separate implicits. So the code complies if the compiler can build one composite evidence for a triplet LeftArgType-RightArgType-ResultType.
the problem specifically is that Sum(Literal(1.1), Literal(1)) has a Literal[Double] on the left and a Literal[Int] on the right. The LUB of Int and Double is indeed AnyVal as you have seen.
https://scalafiddle.io/sf/ALM9urR/1
works perfectly fine. I also think this is good behavior because adding different types can be a bit iffy but else you could introduce an implicit that lets you do the necessary conversions.

how to define upper bounds for scala method

how do you define a Scala method such that it would take in the subclass of any type A without throwing a compilation error?
trait A
case class B extends A
case class C extends A
case class W[T](abc: Option[T]= None)
def methodOne(a: A): W[A] = {
a match {
case b:B => methodTwo() // throws compilation error
case c:C => methodThree() // throws compilation error
}
}
def methodTwo(): W[B] = y
def methodThree(): W[C] = z
Have tried something like
def methodOne[T <: A](a: A): W[T]
but it doesn't allow to compile still
If you want forall T <: A to imply W[T] <: W[A], you need to make W covariant:
case class W[+T](abc: Option[T] = None)
object X {
def methodOne(a: A): W[A] = {
a match {
case b: B => methodTwo()
case c: C => methodThree()
}
}
def methodTwo(): W[B] = ???
def methodThree(): W[C] = ???
}
For basic coverage of variance, see this post.
You need to make W covariant. You can do this easily by defining it as W[+T]:
case class W[+T](abc: Option[T] = None)
This way if B is a subtype of A, W[B] is also a subtype of W[A].
Option for example is defined as Option[+T], therefore Option[B] is a subtype of Option[A].
You can checkout the official scala docs for more details

What is wrong with the following scala code

I am getting the following compile time error in the code below
Error:(7, 29) not found: value Cons
def ::[B >: A](head: B) = Cons[B](head, this)
package basics
sealed trait List[+A] {
import Types._
def ::[B >: A](head: B) = Cons[B](head, this)
def foreach(f: A => Unit): Unit = {
this match {
case x :: t => {
f(x)
t foreach f
}
case Nil => ()
}
}
}
object Types {
type Cons[A] = ::[A]
}
case class ::[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
object Application {
def main(args: Array[String]): Unit ={
println("hello")
3 :: Nil
}
}
Cons is a type alias not a value. It cannot occur on value position. For example:
I made a few modifications to your program to make it work:
case class ::[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
object Types {
type Cons[A] = ::[A]
def cons[A](head: A, tail: List[A]) = ::(head,tail)
}
sealed trait List[+A] {
import Types._
def ::[B >: A](head: B):Cons[B] = cons[B](head, this)
}
In def ::[B >: A](head: B):Cons[B] = cons[B](head, this), :Cons[B] illustrates one correct use case of a type alias.
Another problem with your program is the existence of two :: overloaded symbols in the same scope, that's why it required the creation of Types.cons, otherwise the Scala compiler thinks we are trying to invoke List#::
Here is a example from the REPL:
scala> 3 :: Nil
res0: Types.Cons[Int] = ::(3,Nil$#46fd71)
scala> 3 :: 4 :: Nil
res1: Types.Cons[Int] = ::(3,::(4,Nil$#46fd71))
See the type of the expressions is Cons[Int].
The reason is that
object Types {
type Cons[A] = ::[A]
}
is declaration of a type, but Cons in
def ::[B >: A](head: B) = Cons[B](head, this)
is reference to constructor. If you replace it with reference to actual constructor.
Add method def Cons[A] = ::[A] to Types and all will work just fine.
A better alternative to defining def Cons[A] = ::[A] is val Cons = ::. This allows not just writing Cons[B](head, this), but pattern matching
this match {
case Cons(x, t) => ...
case Nil => ...
}
and access to any other methods defined on the :: companion object using Cons name. This is also what Scala standard library does:
type Map[A, +B] = immutable.Map[A, B]
type Set[A] = immutable.Set[A]
val Map = immutable.Map
val Set = immutable.Set

Dealing with Function1 type parameters in trait

I have a problem with having a Function1 in my trait and make things work correctly, let's say I have the following trait
trait Foo[+X, +O, Z<:X]{
def bar: Z => O
def args:Option[Z]
}
case class A1(args:Option[String] = None) extends Foo[String,String,String]{
def bar = (x:String) => x+"..."
}
case class A2(args:Option[Int] = None) extends Foo[Int,String,Int]{
def bar = (x:Int) => x.toString
}
val mylist = List(A1(),A2())
The reason that I have type Z is for type signature of Function1 which is [T1-,R+]. I want mylist has type of Foo[Any,Any,Any] now it is Foo[Any,Any,_ >: Int with String]. So basically I want Z to be covariant too which is impossible. How should I define my trait to solve these problems?