java generics in scala sometimes fail with cast exception - scala

So this is a pretty specific problem, but might be related to issues other may have experience, though I could not find a good solution.
in our specific case, we are using Elasticsearch. the problematic piece of code is this:
val hit: org.elasticsearch.search.SearchHit = {...}
val innerSystemField: Option[Long] =
Try(hit.field("system.innerField").getValue) match {
case Success(x) => Some(x.asInstanceOf[Long])
case Failure(e) => {
logger.error("exception during innerField retrieval", e)
None
}
}
so, it doesn't really matters we are using Elasticsearch, what matter, is the library API. so, here's the SearchHit interface's field method which returns an instance of SearchHitField. and here's the SearchHitField interface's getValue method. the method is declared as:
<V> V getValue();
and this is where the problem lies. the mapping which we defined Elasticsearch with, guarantee that the returned value will always be a Long. but every once in a while we get a Failure containing a java.lang.ClassCastException: java.lang.Long cannot be cast to scala.runtime.Nothing$. The thing is, that if I explicitly write the type, it won't compile:
[error] /home/me/projects/my-project/src/main/scala/com/org/project/MyElasticsearchCode.scala:123: polymorphic expression cannot be instantiated to expected type;
[error] found : [V]()V
[error] required: Long
[error] Try[Long](hit.field("system.innerField").getValue) match {
[error] ^
[error] one error found
[error] (project/compile:compileIncremental) Compilation failed
[error] Total time: 4 s, completed Jul 27, 2015 4:16:41 PM
So how do I get around this problem?

I find the message of the ClassCastException a bit confusing. Maybe you could try this:
val innerSystemField: Option[Long] =
Try[Any](hit.field("system.innerField").getValue) match {
case Success(x: java.lang.Long) => Some(x.asInstanceOf[Long])
case Failure(e) => {
logger.error("exception during innerField retrieval", e)
None
}
}

Related

Cannot prove that "a Tuple2" <:< (T, U)

I am trying to build a map from an input map, but the compiler is unable to prove that a 2-element tuple is a 2-element tuple.
Code
class Element[T] extends AnyRef { }
class Sample
{
def makeList(x:Int): Element[_] = {
x match {
case 1 => new Element[Boolean]
case 2 => new Element[(Boolean, Boolean)]
}
}
val input = Map(1 -> "one",2 -> "two")
val output = input.map(e => e._1 -> makeList(e._1)).toMap
}
sbt compile
sbt:root> ~compile
[info] Compiling 1 Scala source to /Users/tda0106/test/scala/target/scala-2.12/classes ...
[error] /Users/tda0106/test/scala/src/main/scala/Test.scala:14:57: Cannot prove that (Int, Element[_$1]) forSome { type _$1 } <:< (T, U).
[error] val output = input.map(e => e._1 -> makeList(e._1)).toMap
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 1 s, completed Jun 27, 2019, 2:38:14 PM
It appears that the problem is related to the forSome { type _$1 }, as otherwise it should match. When I first tried to reproduce it, I used List instead of Element and it compiled. It appears that the different is that List is declared as List[+T] and the + is important here.
Element is from a third party library, so changing it is difficult.
What is the problem that I am running into here, and is there a simple way to fix it?
Scala version: 2.12.8
Scala gets fickle about type inference sometimes when you're doing things with existentials (which is what Element[_] is). A quick explicit type signature will fix that right up.
val output = input.map(e => e._1 -> makeList(e._1)).toMap[Int, Element[_]]
All you're doing is telling the compiler what types you want for the keys and values. The reasons why it can't infer this are long and complicated, but as a general rule once you start throwing underscores in your types, you're going to lose some inference capabilities.

Writing a scala method that can accept subclass of RuntimeException

I want to write a scala method which can take any child of RuntimeException. I have it but it does not compile. What is wrong in the code?
def testme(e: RuntimeException): String = {
case e:BadRequestException=> "bad request"
case e: IllegalArgumentException=>"illegal argument"
}
I get the below error
missing parameter type for expanded function
[error] The argument types of an anonymous function must be fully known. (SLS 8.5)
[error] Expected type was: String
[error] def testme(e: RuntimeException): String = {
[error] ^
[error] one error found
[error] (playWeb/compile:compileIncremental) Compilation failed
[error] Total time: 5 s, completed Sep 21, 2017 2:45:09 PM
You have to specify what you are matching on, e.g. add an e match:
def testme(e: RuntimeException): String = e match {
case e:BadRequestException=> "bad request"
case e: IllegalArgumentException=>"illegal argument"
}

Implementing custom foreach in order to better understand types and function composition

In order to try and understand Scala's type system I'm attempting
to implment a custom implementation for List.foreach method :
package com
object customForEach extends App {
class customForEach[B, A] extends Iterable[A] with collection.Seq[A] {
def foreach[B](f: A ⇒ B) {
var these = this
while (!these.isEmpty) {
f(these.head)
these = these.tail
}
}
def tail = this match {
case h :: t ⇒ t
}
}
}
When I complile this code I receive errors :
[error] \Desktop\Scala\src\main\scala\typeparam.scala:16: constructor cannot be instantiated to expected type;
[error] found : scala.collection.immutable.::[B(in class ::)]
[error] required: com.customForEach.customForEach[B(in class customForEach),A]
[error] case h :: t ? t
[error] ^
[error] \Desktop\Scala\src\main\scala\typeparam.scala:16: not found: value t
[error] case h :: t ? t
[error] ^
[error] \Desktop\Scala\src\main\scala\typeparam.scala:11: type mismatch;
[error] found : Seq[A]
[error] required: com.customForEach.customForEach[B,A]
[error] these = these.tail
[error] ^
[error] three errors found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed 31-Jan-2015 11:53:40
In particular I find it iteresting how println can be composed with List in this fashion : List(1,2,3).foreach(println)
Do I need to add extend another trait in order to access the .tail function ?
For this error :
not found: value t
[error] case h :: t ? t
Shouldn't t be found since it is created using pattern match operator :: ?
There are many reasons why this code won't work. In order to understand the first compiler error not found: value t, you must look at the error immediately before it. :: exists solely for List, but here you do not have a List, only Iterable with Seq. That pattern match can't work, which causes t to become "not found".
There are even larger problems than that, though. Even if you remove your definition of tail (which is unnecessary), you'll then find that you're missing abstract method definitions for apply, iterator, and length from the Seq trait. I imagine you're doing this because you can't extend List, which is sealed. You can copy the implementations of apply, and length from LinearSeqOptimized, then easily implement an iterator method, but there's still another problem: your class does not have a constructor.
Okay, well we'll look at what List does again. List is abstract and has two sub-types, :: and Nil. Nil is just a case object, and :: has a constructor that accepts the head and tail of the List. This isn't going to help you very much, unless you also want to duplicate the code for :: and Nil as well.
Scala collections are very large complicated beasts, and extending them to override one method is not a simple process.

slick 2.0 define generic `find by field` method

import scala.slick.driver.MySQLDriver.simple._
class RichTable[T](tag: Tag, name: String) extends Table[T](tag, name) {
case class QueryExt[B](q: Query[RichTable.this.type, B]) {
def whereEq[C](col: RichTable.this.type => Column[C], c: C) = {
q.filter { fields =>
col(fields) === c
}
}
}
}
Then it complains
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:10: value === is not a member of slick.driver.MySQLDriver.simple.Column[C]
[error] col(fields) === c
[error] ^
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:9: ambiguous implicit values:
[error] both value BooleanColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Boolean]]
[error] and value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Option[Boolean]]]
[error] match expected type scala.slick.lifted.CanBeQueryCondition[Nothing]
[error] q.filter { fields =>
[error] ^
[error] two errors found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed Mar 6, 2014 1:21:48 AM
There have been questions about this, but the answers did not work for 2.0
How to parametrize Scala Slick queries by WHERE clause conditions?
Slick doesn't have any information about C, so it doesn't know if it can and how it should map it to a database value and if it can use === on it. So you get a type error. You will have to use Scala's type system to restrict the type to one for which Slick knows how to map it. You can do this by providing a so-called Context Bound, in this case :BaseColumnType.
def whereEq[C:BaseColumnType](col: RichTable.this.type => Column[C], c: C) = {
q.filter { fields =>
col(fields) === c
}
}
BaseColumnType is provided by Slick and using it in this way basically tells the Scala compiler to look for an implicit value of type BaseColumnType[C] in scope, where you call whereEq. Because then it is usually known what C will actually be. Slick comes with BaseColumnType[Int], BaseColumnType[String], etc. so at the call site, the Scala compiler can find one when your C is really an Int or String in that particular call and this way pass the info further to Slick.
Same for LiuTiger's question. abstract class Crud[..., PK:BaseColumnType] should do the trick, a trait doesn't work with context bounds. When implementing an abstract DAO be prepared to face a lot of challenges and get to the edges of your Scala type system skills and learn quite a bit about type inference order, implicit parameters, etc.

Play framework asynchronous results

I changed one function from:
def submit = Action { request =>
signupForm.bindFromRequest()(request).fold(
// Form has errors
errors => BadRequest(html.signup.form(errors)),
// We got a valid User value, display the summary
user => {
// intensive computation involving database
Ok("okay")
}
)
}
to
def submit = Action { request =>
val result = Akka.future {
signupForm.bindFromRequest()(request).fold(
// Form has errors
errors => BadRequest(html.signup.form(errors)),
// We got a valid User value, display the summary
user => {
// intensive computation involving database
Ok("okay")
}
)
}
Async {
result
}
}
and I get the compilation error of:
[error] found : play.api.mvc.SimpleResult[_ >: java.lang.String with play.api.templates.Html <: java.io.Serializable]
[error] required: play.api.mvc.SimpleResult[_1(in value result)] where type _1(in value result) >: java.lang.String with play.api.templates.Html <: java.io.Serializable
[error] Note: java.io.Serializable >: _1, but class SimpleResult is invariant in type A.
[error] You may wish to define A as -A instead. (SLS 4.5)
[error] signupForm.bindFromRequest()(request).fold(
[error] ^
[error] one error found
The error message seem like it has something to do with variance. Does anyone understand what's going on?
BadRequest is returning the type SimpleResult[Html]
Ok is returning the type SimpleResult[String]
If you make BadRequest and Ok return the same type then it would work.
Try doing Ok(Html("ok")) - or actually render a page.