I am new to scala & IntelliJ, so this might sound like a stupid question. However, I see that when debugging, IntelliJ will infer parameter types to some kind of "weird" type, in my case MapLike$MappedValues. The expected type would be Map[String, Iterable[Person]].
Why can IntelliJ not display the correct type? Right now this would be kind of important to me, because by debugging I am trying to find out the correct parameter type, because the API documentation on this part is not very clear (working with Apache Flink CEP)
Code example:
val result:DataStream[String] = patternStream.select(patterns => {
val person:Person = patterns.head._2.head
s"Person ${person.name} of age ${person.age} can drink!"
})
This is what I can see in the debugger:
According to the documentation:
"The select() method takes a selection function as argument, which is called for each matching event sequence. It receives a match in the form of Map[String, Iterable[IN]]"
Why does IntelliJ display MapLike$MappedValues and not something like Map[String,Iterable[Person]]?
The debugger is showing you the runtime class.
On the JVM, generic type parameters are "erased" at runtime, so they will not be visible in the debugger. Map is just an interface, but MapLike$MappedValues implements it as inheritor of AbstractMap.
Thus, the displayed class is compatible with the type of the value.
To find the compile time type of an expression, IntelliJ can help you:
select the expression
press the "type info" hotkey (usually shift+ctrl+p)
Related
I want to use sttp library with guice(with scalaguice wrapper) in my app. But seems it is not so easy to correctly bind things like SttpBackend[Try, Nothing]
SttpBackend.scala
Try[_] and Try[AnyRef] show some other errors, but still have no idea how it should be done correctly
the error I got:
kinds of the type arguments (scala.util.Try) do not conform to the expected kinds of the type parameters (type T).
[error] scala.util.Try's type parameters do not match type T's expected parameters:
[error] class Try has one type parameter, but type T has none
[error] bind[SttpBackend[Try, Nothing]].toProvider[SttpBackendProvider]
[error] ` ^
SttpBackendProvider looks like:
def get: SttpBackend[Try, Nothing] = TryHttpURLConnectionBackend(opts)
complete example in scastie
interesting that version scalaguice 4.1.0 show this error, but latest 4.2.2 shows error inside it with converting Nothing to JavaType
I believe you hit two different bugs in the Scala-Guice one of which is not fixed yet (and probably even not submitted yet).
To describe those issues I need a fast intro into how Guice and Scala-Guice work. Essentially what Guice do is have a mapping from type onto the factory method for an object of that type. To support some advanced features types are mapped onto some internal "keys" representation and then for each "key" Guice builds a way to construct a corresponding object. Also it is important that generics in Java are implemented using type erasure. That's why when you write something like:
bind(classOf[SttpBackend[Try, Nothing]]).toProvider(classOf[SttpBackendProvider])
in raw-Guice, the "key" actually becomes something like "com.softwaremill.sttp.SttpBackend". Luckily Guice developers have thought about this issue with generics and introduced TypeLiteral[T] so you can convey the information about generics.
Scala type system is more reach than in Java and it has some better reflection support from the compiler. Scala-Guice exploits it to map Scala-types on those more detailed keys automatically. Unfortunately it doesn't always work perfectly.
The first issue is the result of the facts that the type SttpBackend is defined as
trait SttpBackend[R[_], -S]
so it uses it expects its first parameter to be a type constructor; and that originally Scala-Guice used the scala.reflect.Manifest infrastructure. AFAIU such higher-kind types are not representable as Manifest and this is what the error in your question really says.
Luckily Scala has added a new scala.reflect.runtime.universe.TypeTag infrastructure to tackle this issue in a better and more consistent way and the Scala-Guice migrated to its usage. That's why with the newer version of Scala-Guice the compiler error goes away. Unfortunately there is another bug in the Scala-Guice that makes the code fail in runtime and it is a lack of handling of the Nothing Scala type. You see, the Nothing type is a kind of fake one on the JVM. It is one of the things where the Scala type system is more reach than the Java one. There is no direct mapping for Nothing in the JVM world. Luckily there is no way to create any value of the type Nothing. Unfortunately you still can create a classOf[Nothing]. The Scala-to-JVM compiler handles it by using an artificial scala.runtime.Nothing$. It is not a part of the public API, it is implementation details of specifically Scala over JVM. Anyway this means that the Nothing type needs additional handling when converting into the Guice TypeLiteral and there is none. There is for Any the cousin of Nothing but not for Nothing (see the usage of the anyType in TypeConversions.scala).
So there are really two workarounds:
Use raw Java-based syntax for Guice instead of the nice Scala-Guice one:
bind(new TypeLiteral[SttpBackend[Try, Nothing]]() {})
.toInstance(sttpBackend) // or to whatever
See online demo based on your example.
Patch the TypeConversions.scala in the Scala-Guice as in:
private[scalaguice] object TypeConversions {
private val mirror = runtimeMirror(getClass.getClassLoader)
private val anyType = typeOf[Any]
private val nothingType = typeOf[Nothing] // added
...
def scalaTypeToJavaType(scalaType: ScalaType): JavaType = {
scalaType.dealias match {
case `anyType` => classOf[java.lang.Object]
case `nothingType` => classOf[scala.runtime.Nothing$] //added
...
I tried it locally and it seems to fix your example. I didn't do any extensive tests so it might have broken something else.
The following type of code is quite frequent in scala method parameters:
events: Map[BpmId, Seq[BpmEvent]] = Map.empty[BpmId, Seq[BpmEvent]],
So theoretically after having provided the type information and the Map.empty the IDE should be able to fill in the rest for us:
events: Map[BpmId, Seq[BpmEvent]] = Map.empty[**IDE fills in the types for us**],
However I have not discovered an Intellij built-in or plugin function to provide that feature. Is there such an animal?
For method arguments, the use of Map() as a default empty-Map value is an option. Type information is preserved.
This is a non working try for using Flink fold with scala anonymous function:
val myFoldFunction = (x: Double, t:(Double,String,String)) => x + t._1
env.readFileStream(...).
...
.groupBy(1)
.fold(0.0, myFoldFunction : Function2[Double, (Double,String,String), Double])
It compiles well, but at execution, I get a "type erasure issue" (see below). Doing so in Java is fine, but of course more verbose. I like the concise and clear lambdas. How can I do that in scala?
Caused by: org.apache.flink.api.common.functions.InvalidTypesException:
Type of TypeVariable 'R' in 'public org.apache.flink.streaming.api.scala.DataStream org.apache.flink.streaming.api.scala.DataStream.fold(java.lang.Object,scala.Function2,org.apache.flink.api.common.typeinfo.TypeInformation,scala.reflect.ClassTag)' could not be determined.
This is most likely a type erasure problem.
The type extraction currently supports types with generic variables only in cases where all variables in the return type can be deduced from the input type(s).
The problem you encountered is a bug in Flink [1]. The problem originates from Flink's TypeExtractor and the way the Scala DataStream API is implemented on top of the Java implementation. The TypeExtractor cannot generate a TypeInformation for the Scala type and thus returns a MissingTypeInformation. This missing type information is manually set after creating the StreamFold operator. However, the StreamFold operator is implemented in a way that it does not accept a MissingTypeInformation and, consequently, fails before setting the right type information.
I've opened a pull request [2] to fix this problem. It should be merged within the next two days. By using then the latest 0.10 snapshot version, your problem should be fixed.
[1] https://issues.apache.org/jira/browse/FLINK-2631
[2] https://github.com/apache/flink/pull/1101
I've made use of a few of scala's built-in type classes, and created a few of my own. However, the biggest issue I have with them at the moment is: how do I find type classes available to me? While most of those that I write are small and simple, it would be nice to know if something already exists that does what I'm about to implement!
So, is there a list, somewhere, of all the type classes or implicit values available in the standard library?
Even better, is it possible to somehow (probably within the REPL) generate a list of the implicit values available in the current scope?
It's a job for a good IDE.
IntellijIDEA 14+
Check out Implicits analyser in Scala Plugin 1.4.x. Example usage:
def myMethod(implicit a: Int) = {
}
implicit val a: Int = 1
myMethod // click the myMethod and press Ctrl+Shift+P, the "Implicit Parameters" is shown
Eclipse
Check out Implicit highlighting.
Scala REPL
You can list implicits like this:
:implicits -v
And investigate their origin like defined here:
import reflect.runtime.universe
val tree = universe.reify(1 to 4).tree
universe.showRaw(tree)
universe.show(tree)
At the moment, I type in type annotations for public vals, vars and defs in my Scala classes, traits and objects[1] - either by inferring the types of them mentally, or occasionally by hovering over the identifier in Eclipse to find out what the presentation compiler thinks the type should be[2]. How can I conveniently add these type annotations automatically?
The purpose of adding in explicit types is to "lock in" the type of a public member of a template, so that if a developer changes the definition of the member in future in a way that results in an incompatible type, they will get a compile-time error unless they deliberately change the type annotation as well.
Footnotes:
[1] except if they override a member in a supertype and the type should be the same as the type of the overridden member, which is usually the case for overrides in my code.
[2] This isn't always correct; the presentation compiler seems to be weak when it comes to members that override members in supertypes.
This has been implemented in the Scala IDE in Kepler Eclipse 4.3 update.
Use ctrl/cmd-1 on the identifier and choose "Add explicit type ...".
See here.
For those who use IDEA on Mac, the following works:
⌥ (Alt) + return
That is, press and hold Alt and hit return/enter.
Then select "Add type annotation to value definition".
There is a feature request for a quick fix to insert inferred type in declarations (scala-ide#1433), but there has not been any contributions on it yet.
This is not direct solution to your problem, but if you unit test those functions then your tests will "enforce" return types and will break when someone changes return types.
For example, in specs2 it could look something like:
foo(arg1) must be equalTo Success
Another partial solution is the scalastyle SBT plugin (http://www.scalastyle.org/rules-0.2.0.html) which can warn you about public members with inferred types.
In Eclipse: Hover over the identifier, click to expand the hover, triple-click to select all, Ctrl+C (or Cmd+C) to copy, click on the code to remove the hover, click back to where you were, Ctrl+V to paste, then finally delete all the wrong/over-verbose/redundant stuff.
It'd probably be quicker to just type in the correct type.
Does anyone have a better way?
Using IDEA Intellij 15: File menu > Settings > Editor > Intentions
Minimize the Intentions and select Scala > Type and make sure that "Toggle Type Annotation" is checked.
Then when you select a (variable or function) definition and hit Alt+[Enter] it asks you if you want to insert the inferred type - hit [carriage-return] to do so.
I know that's 2 key-strokes, but it's still better than selecting, copying and pasting.