In response to my question How to develop macro to short-circuit null? someone pointed me to an earlier long thread with many answers, the most compelling of which to was https://stackoverflow.com/a/5569905 . But it doesn't quite work with Scala AnyVal "primitives" like Int:
object TestOp {
class SafeDereference[A](obj: A) {
def ?[B >: Null](function: A => B): B = if (obj == null) null else function(obj)
}
implicit def safeDereference[A](obj: A) = new SafeDereference(obj)
class C {
def getValue: Int = 0
}
def main(args: Array[String]) {
val c = new C
val x:Int = c ? (_.getValue)
}
}
gives a compilation error of:
[error] TestOp.scala:14: type mismatch;
[error] found : Any
[error] required: Int
[error] val x:Int = c ? (_.getValue)
[error] ^
[error] one error found
[error] {file:/home/mmalak/streaming-demo/}default-ae36bd/compile:compile: Compilation failed
A workaround is to replace val x:Int with val x:java.lang.Integer, and that will compile. Is there a way to improve SafeDereference above so that val x:Int is allowed?
Additional information
The following produces the desired output. The question now becomes how to move the typecasts into SafeDereference, and how to handle all the other Scala "primitives" (Boolean etc).
object TestOp {
class SafeDereference[A](obj: A) {
def ?[B >: Null](function: A => B): B = if (obj == null) null else function(obj)
}
implicit def safeDereference[A](obj: A) = new SafeDereference(obj)
class C {
def getValue: Int = 0
}
def main(args: Array[String]) {
val c:C = null
val x = (c ? (_.getValue)).asInstanceOf[java.lang.Integer].asInstanceOf[Int]
println("x="+x)
}
}
outputs, as desired:
x=0
You could do something like this. The Zero trait allows you to determine the zero value for any object that is not nullable. In this case I added one for Numeric types:
object TestOp {
trait Zero[T] {
def apply(): T
}
object Zero {
implicit def zeroNull[B >: Null] =
new Zero[B] { def apply = null }
implicit def zeroNumeric[B: Numeric] =
new Zero[B] { def apply = implicitly[Numeric[B]].zero }
}
implicit class SafeDereference[A](obj: A) {
def ?[B](function: A => B)(implicit zero: Zero[B]): B =
if (obj == null) zero() else function(obj)
}
class C {
def getValue: Int = 0
def getSomething: C = new C
}
def main(args: Array[String]) {
val c = new C
val x = c ? (_.getValue)
val y = c ? (_.getSomething)
}
}
Edit
For Boolean you would add something like this:
implicit def zeroBoolean[B >: Boolean] =
new Zero[B] { def apply = false }
Related
I have two sorted lists, and want to create an iterator which will merge the two lists in sorted oder. I'm new to Scala and especially new with writing generic classes. I get a compile error that I'm unable to figure out.
Here is the code for MergingIterator
class MergingIterator[ElementType <: Ordered[ElementType]] (iterator1: BufferedIterator[ElementType], iterator2: BufferedIterator[ElementType]) extends Iterator[ElementType]{
override def hasNext: Boolean = {
iterator1.hasNext || iterator2.hasNext
}
override def next(): ElementType = {
if(!iterator1.hasNext && !iterator2.hasNext){
throw new IllegalStateException("All iterators empty. No next element")
}
val e1 : Option[ElementType] = if(iterator1.hasNext) Some(iterator1.head) else None
val e2 : Option[ElementType] = if(iterator2.hasNext) Some(iterator2.head) else None
if(e1.isDefined && e2.isDefined){
val e1a = e1.get
val e2a = e2.get
if(e1a.equals(e2a) || e1a < e2a){
iterator1.next()
}
else {
iterator2.next()
}
}
else if (e1.isDefined){
iterator1.next
}
else if (e2.isDefined){
iterator2.next()
}
else{
throw new Exception("InvalidState. No elements present")
}
}
}
And here is the Unit Test, which is what doesn't compile:
class MergingIteratorTest extends AnyFunSuite {
test("Both Iterators Empty"){
runTest(List(), List(), List())
}
private def runTest(vals1 : List[ComparableInt], vals2: List[ComparableInt], expected: List[ComparableInt]): Unit ={
val it1 = vals1.iterator.buffered
val it2 = vals2.iterator.buffered
val merging = new MergingIterator[ComparableInt](it1, it2)
val merged = merging.toList
assert(expected.equals(merged))
}
case class ComparableInt(value: Int) extends Ordered[Int] {
override def compare(that: Int): Int = value - that
}
}
However when I try to compile, I get the following errors.
Error:(14, 9) type arguments [MergingIteratorTest.this.ComparableInt] do not conform to class MergingIterator's type parameter bounds [ElementType <: Ordered[ElementType]]
val merging = new MergingIterator[ComparableInt](it1, it2)
Error:(14, 23) type arguments [MergingIteratorTest.this.ComparableInt] do not conform to class MergingIterator's type parameter bounds [ElementType <: Ordered[ElementType]]
val merging = new MergingIterator[ComparableInt](it1, it2)
I'm sure it's something stupid that I'm doing wrong, but due to my lack of experience with scala I've been able to figure out what the problem is. Any help here would be appreciated.
You need to make
case class ComparableInt(value: Int) extends Ordered[ComparableInt] {
override def compare(that: ComparableInt): Int = value.value - value.value
}
The issue is in [ElementType <: Ordered[ElementType]] type bounds - you see ElementType should be ordered to itself and your case ComparableInt implements Ordered[Int] and meaning ComparableInt is not Ordered to itself as MergingIterator signature expects.
Same thing with the Ordering type class.
class MergedIterator[ElmTyp:Ordering](itrA: Iterator[ElmTyp]
,itrB: Iterator[ElmTyp]
) extends Iterator[ElmTyp] {
import Ordering.Implicits._
private val itr1 = itrA.buffered
private val itr2 = itrB.buffered
override def hasNext: Boolean = itr1.hasNext || itr2.hasNext
override def next(): ElmTyp =
if (!itr1.hasNext) itr2.next()
else if (!itr2.hasNext) itr1.next()
else if (itr1.head > itr2.head) itr2.next()
else itr1.next()
}
I want to implement generic and typesafe domain repository. Say I have
trait Repo[Value] {
def put(value: Value): Unit
}
case class IntRepo extends Repo[Int] {
override def put(value: Int): Unit = ???
}
case class StringRepo extends Repo[String] {
override def put(value: String): Unit = ???
}
case class DomainRepo(intRepo: IntRepo, stringRepo: StringRepo) {
def putAll[?](values: ?*): Unit // what type should be here?
}
As result I want to have following api:
domainRepo.putAll(1, 2, 3, "foo", "bar") //Should work
domainRepo.putAll(1, 2, true, "foo") // should not compile because of boolean value
The question is How to achieve this?
so, I see only one way to make it typesafe. It's to do pattern matching on Any type like
def putAll(values: Seq[Any]) => Unit = values.foreach {
case str: String => stringRepo.put(str)
case int: Int => intRepo.put(int)
case _ => throw RuntimeException // Ha-Ha
}
but what if I would have 10000 of types here? it would be a mess!
another not clear for me approach for now is to use dotty type | (or) like following:
type T = Int | String | 10000 other types // wouldn't be a mess?
def putAll(t: T*)(implicit r1: Repo[Int], r2: Repo[String] ...) {
val myTargetRepo = implicitly[Repo[T]] // would not work
}
so, what do you think? is it even possible?
the easies way I've saw was
Map[Class[_], Repo[_]]
but this way allows to do a lot of errors
It seems you are looking for a type class
trait Repo[Value] {
def put(value: Value): Unit
}
implicit val intRepo: Repo[Int] = new Repo[Int] {
override def put(value: Int): Unit = ???
}
implicit val stringRepo: Repo[String] = new Repo[String] {
override def put(value: String): Unit = ???
}
case object DomainRepo {
def putAll[Value](value: Value)(implicit repo: Repo[Value]): Unit = repo.put(value)
}
If you want domainRepo.putAll(1, 2, 3, "foo", "bar") to compile and domainRepo.putAll(1, 2, true, "foo") not to compile, you can try to use heterogeneous collection (HList).
import shapeless.{HList, HNil, ::, Poly1}
import shapeless.ops.hlist.Mapper
trait Repo[Value] {
def put(value: Value): Unit
}
implicit val intRepo: Repo[Int] = new Repo[Int] {
override def put(value: Int): Unit = ???
}
implicit val stringRepo: Repo[String] = new Repo[String] {
override def put(value: String): Unit = ???
}
case object DomainRepo {
def put[Value](value: Value)(implicit repo: Repo[Value]): Unit = repo.put(value)
object putPoly extends Poly1 {
implicit def cse[Value: Repo]: Case.Aux[Value, Unit] = at(put(_))
}
def putAll[Values <: HList](values: Values)(implicit
mapper: Mapper[putPoly.type, Values]): Unit = mapper(values)
}
DomainRepo.putAll(1 :: 2 :: 3 :: "foo" :: "bar" :: HNil)
// DomainRepo.putAll(1 :: 2 :: true :: "foo" :: HNil) // doesn't compile
I have an array of Any (in real life, it's a Spark Row, but it's sufficient to isolate the problem)
object Row {
val buffer : Array[Any] = Array(42, 21, true)
}
And I want to apply some operations on its elements.
So, I've defined a simple ADT to define a compute operation on a type A
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
Given that I have a list of all operations and I know which operation is to apply to each element, let's use these operations.
object GenericsOp {
import Row._
val ops = Seq(Count, Exist)
def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}
By design, for a given op, types are aligned between cast and combine. But unfortunately the following code does not compile. The error is
Type mismatch, expected: _$1, actual: AnyVal
Is there a way to make it work ?
I've found a workaround by using abstract type member instead of type parameter.
object AbstractOp extends App {
import Row._
trait Op {
type A
def compute(a: A) : A
}
case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}
val ops = Seq(Count, Exist)
def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}
Is there a better way ?
It seems that your code can be simplified by making Op[A] extend Any => A:
trait Op[A] extends (Any => A) {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
def apply(a: Any): A = compute(cast(a))
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i)(buffer(i))
}
println(buffer.mkString("[", ",", "]"))
}
}
Since it's asInstanceOf everywhere anyway, it does not make the code any less safe than what you had previously.
Update
If you cannot change the Op interface, then invoking cast and compute is a bit more cumbersome, but still possible:
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i) match {
case op: Op[t] => op.compute(op.cast(buffer(i)))
}
}
println(buffer.mkString("[", ",", "]"))
}
}
Note the ops(i) match { case op: Opt[t] => ... } part with a type-parameter in the pattern: this allows us to make sure that cast returns a t that is accepted by compute.
As a more general solution than Andrey Tyukin's, you can define the method outside Op, so it works even if Op can't be modified:
def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))
buffer(0) = apply(ops(0), buffer(0))
Please consider the following trivial example:
class C[P](val list: Seq[P]){
def print(e: P){
println(e)
}
}
object Test{
def g[P](c: C[P]) = {
c.print(c.list(0))
}
def f(i: Int): C[_] = {
i match {
case 1 => new C(Seq(1, 2, 3))
case _ => new C(Seq("A", "B", "C"))
}
}
def main(args: Array[String]): Unit = {
val c = f(1)
g(c) // works
c.print(c.list(0)) // does not work
}
}
My question is, in the main function, why the first call compiles, but the second gives "type mismatch" error.
Is there any other (better) way to do what is intended here?
Edit 1:
According to the answer by #chengpohi, I can change the type of the returned value of f to C[Any], but this generally may not work. For example, if we change the code to
class B[P]
class BInt extends B[Int]
class BString extends B[String]
class C[P](val list: Seq[P], val obj: B[P]) {
def print(e: P) {
println(e)
}
}
object Test {
def g[P](c: C[P]) = {
c.print(c.list(0))
}
def f(i: Int): C[_] = {
i match {
case 1 => new C(Seq(1), new BInt)
case _ => new C(Seq("String"), new BString)
}
}
def main(args: Array[String]): Unit = {
val c = f(1)
g(c) // works
c.print(c.list(0)) // does not work
}
}
I cannot change the return type of f to C[Any] anymore ("type mismatch").
def f(i: Int): C[Any] = {
i match {
case 1 => new C(Seq(1, 2, 3))
case _ => new C(Seq("A", "B", "C"))
}
}
Try to set the f method return type: C[Any] from C[_], for typeC[_], the compiler will translate C[_] to C<?>.
for:
def g[P](c: C[P]) = {
c.print(c.list(0))
}
this method works, this is caused by that we have bound P type in this method g, so the compiler can infer this generic P (Object type).
but in main method:
c.print(c.list(0))
There is no type context for c, and c's type is C[_], but c.list's type is Seq[Any], and for the Generic Type P in the c.print will be thought as _$1 type. so the type mismatch compile error throwed.
You can use type variable patterns to give a name to the type parameter:
f(1) match {
case c: C[a] => c.print(c.list(0))
}
In the code shown below, how can I convert EmptyTree to object (Singleton) ?
trait Tree[T] {
def contains(num: T): Boolean
def inc( num: T ): Tree[T]
}
class EmptyTree[T <% Ordered[T] ] extends Tree[T] {
def contains(num:T):Boolean = false
def inc(num:T):Tree[T] = {
new DataTree(num, new EmptyTree, new EmptyTree)
}
override def toString = "."
}
class DataTree[T <% Ordered[T] ](val x:T, val left:Tree[T], val right:Tree[T]) extends Tree[T] {
def contains(num:T):Boolean = {
if( num < x ) left.contains(x)
else if ( num > x ) right.contains(x)
else true
}
def inc(num:T):Tree[T] = {
if(num < x ) new DataTree(x, left.inc(num), right)
else if ( num > x ) new DataTree(x, left, right.inc(num))
else this
}
override def toString = "{" + left + x + right + "}"
}
val t = new DataTree(20, new EmptyTree[Int], new EmptyTree[Int])
//> t : greeting.Test.DataTree[Int] = {.20.}
val p = t.inc(10) //> p : greeting.Test.Tree[Int] = {{.10.}20.}
val a = p.inc(30) //> a : greeting.Test.Tree[Int] = {{.10.}20{.30.}}
val s = a.inc(5) //> s : greeting.Test.Tree[Int] = {{{.5.}10.}20{.30.}}
val m = s.inc(11) //> m : greeting.Test.Tree[Int] = {{{.5.}10{.11.}}20{.30.}}
Let me detalize Alexey's answer. Here is full implementation with some code style improvements:
First define your trait with aknowledgment of its covariance:
trait Tree[+T] {
def contains[U >: T : Ordering](num: U): Boolean
def inc[U >: T : Ordering](num: U): Tree[U]
}
Next define your subtype-of-all-trees object
case object EmptyTree extends Tree[Nothing] {
def contains[U >: Nothing : Ordering](num: U): Boolean = false
def inc[U >: Nothing : Ordering](num: U): Tree[U] =
DataTree(num, EmptyTree, EmptyTree)
override def toString = "."
}
Now change your general case implementation:
case class DataTree[T: Ordering](x: T, left: Tree[T], right: Tree[T]) extends Tree[T] {
import Ordering.Implicits._
def contains[U >: T : Ordering](num: U): Boolean =
if (num < x) left.contains(x)
else if (num > x) right.contains(x)
else true
def inc[U >: T : Ordering](num: U): Tree[U] =
if (num < x) DataTree(x, left.inc(num), right)
else if (num > x) DataTree(x, left, right.inc(num))
else this
override def toString = "{" + left + x + right + "}"
}
You could be a little bit frustrated since I replaced your Ordered with Ordering, but you should know that view bounds are deprecated
You have to fix the generic argument because that's the only time you can provide it:
scala> trait A[T]
defined trait A
scala> object B extends A[Int]
defined object B
Obviously you want to reuse EmptyTree for all types of T, so instead of defining A[SOMETYPE] for each type just use bottom type Nothing:
scala> object B extends A[Nothing]
defined object B
This object can be used with any tree.
That's exactly how Option[T] is implemented in Scala. Here is how None is defined:
case object None extends Option[Nothing]
If keeping generics, also there is an option to add empty factory - like it's done for Map and Vector. Off course, with such an implementation it will not be a unique instance object for every creation, but when using inc method, it will not produce new objects, it will just reference itself.
object DataTree {
def empty[T <% Ordered[T]] = new Tree[T] {
def contains(num: T):Boolean = false
def inc(num: T): Tree[T] = {
new DataTree(num, this, this)
}
override def toString = "."
}
}
So you can instantiate it as following:
val t = new DataTree(20, DataTree.empty[Int], DataTree.empty[Int])