What to do to make this compile?


#1

Hello,

I have some Kotlin beginner question. I wrote my own little select method that does basically the same as the Collection.filter method. Problem is that I don’t understand the compilation error in the code below. I don’t really see why the Collection.filter counterpart compiles fine whereas my select test case doesn’t (see comment compiler errors). Played around for a while but then gave up. Seems there is something I’m missing and won’t find out about that quickly. Any suggestions?

Thanx, Oliver

fun main(args : Array<String>)
{
  val list = ArrayList<Int>()
  list.add(1)
  list.add(2)
  list.add(3)

  var filteredList = list.filter({ item -> item >= 2})   // compiles fine
  var otherFilteredList : List<Int> = list.filter({ item -> item >= 2})   // compiles fine
  var folteredCollection : Collection<Int> = list.filter({ item -> item >= 2})   // compiles anyway

  var selectList = list.select({item -> item >= 2})  // compiles fine
  var selectCollection : Collection<Int> = list.select({ item -> item >= 2})  // compiles fine
  var otherSelectList : List<Int> = list.select({ item -> item >= 2})   // compiler errors
}

fun <T> Collection<T>.select(condition : (T) -> Boolean) : Collection<T>
{
  var result = newInstanceNotNull()
  for (item in this) {
  if (condition(item))
           result.add(item)
  }
  return result
}

fun <T> Collection<T>.newInstanceNotNull() : Collection<T>
{
  val result = javaClass.newInstance()
  if(result != null)
  return result
  return ArrayList<T>
}


#2

BTW there's no need for the parens around the { } blocks.

list.filter { it > 10 }

What compile error do you get?


#3

As an aside, in general if ever the type inferencer can't figure something out, either add a type to the method parameter...

list.select{ (item: Int) -> item > 5 }

or add a type hint to the method call

list.select<Int>{ it > 5 }

#4

BTW there's no need for the parens around the { } blocks.

This is cool. I didn't know...

What compile error do you get?

The compiler errors are:

Type inference failed: Resulting type is Collection<T> but List<Int> was expected
Unresolved reference: >=
Type mismatch: inferred type is java.util.ArrayList<jet.Int> but java.util.Collection<T> was expected
Type mismatch: inferred type is java.util.Collection<T> but java.util.List<jet.Int> was expected

But I think the problem is also clear without looking at the compiler errors: “fun <T> Collection<T>.select(condition : (T) -> Boolean) : Collection<T>” declares a Collection. So

var selectCollection : Collection<Int> = list.select({ item -> item >= 2})

compiles whereas

var otherSelectList : List<Int> = list.select({ item -> item >= 2})

does not since the compiler can only infer from the type signature from the method declaration and cannot see what type javaClass.newInstance in newInstanceNotNull() will return at runtime. What I am asking myself is why the Collection.filter method does not suffer from this problem… Maybe because it goes with hardcoding ArrayList<T> instead of using javaClass.newInstance(). But I’m not sure, there are quite some other factors that might matter as well.


#5

Have a look at the source :) to see how it works... https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/JLangIterablesLazy.kt#L19

The return type of filter() on Collection is of type List<T>

BTW while surfing the api docs there are source links (right had side) when you hover over a function detail or a snippet of code.


#6

The return type of filter() on Collection is of type List<T>

Right. Stupid me .. Thanx


#7

The return type of filter() on Collection is of type List<T>

That's what the source says but the compiler error says otherwise:

None of the following functions can be called with the arguments supplied: fun <T : jet.Any?>java.util.Iterator<T>.filter(val f : (T) -> jet.Boolean) : java.util.Iterator<T> defined in <java_root>.kotlin fun <T : jet.Any?>jet.Iterable<T>.filter(val predicate : (T) -> jet.Boolean) : java.util.Collection<T> defined in <java_root>.kotlin fun <T : jet.Any?>jet.Array<T>.filter(val predicate : (T) -> jet.Boolean) : java.util.Collection<T> defined in <java_root>.kotlin fun <T : jet.Any?>java.lang.Iterable<T>.filter(val predicate : (T) -> jet.Boolean) : java.util.Collection<T> defined in <java_root>.kotlin.util

And indeed, Collection<T> is the only type annotation that the compiler will accept here:

  var filteredCollection : Collection<Int> = list.filter { item -> item >= 2 }   // compiles anyway

What’s going on?


#8

Yeah, strange ... I was using CE 11.1.1 Build 117.117 and apparently got different compiler errors.


#9

All right, I have another one ... ;-) In the code below I do not really understand why line "-- 1" does not compile. As Bar is a subclass of Foo this should basically be allowed. Interestingly, there is also no error displayed in the IDE at line "--2" also. Do we have the same issue here as in Java where Java treats List<Foo> and List<Bar> as two completely different types and does not look at the class parameter? Just curious in order to understand this.

Regards, Oliver

open class Foo(param : Int) {
  val value = param;
  fun getFoo() : Int {
  return value
  }
}

class Bar(param : Int) : Foo(param) {
  fun getBar() : Int {
  return value
  }
}

fun main(args : Array<String>)
{

  val barList = ArrayList<Bar>
  barList.add(Bar(1))
  barList.add(Bar(2))
  barList.add(Bar(3))

  val filteredBarList : List<Bar> = barList.filter{it.getBar() >= 2} // compiles fine
  val filteredFooList : List<Foo> = barList.filter{it.getBar() >= 2} // does not compile ?! – 1

  filteredFooList.get(0).getFoo();   // compiles fine – 2
  filteredFooList.get(0).getBar();   // does not compile as expected – 3
}


#10

Oops, forgot the compiler errors:

None of the following functions can be called with the arguments supplied:
public final fun <T : jet.Any?>java.util.Iterator<T>.filter(val predicate : (T) -> jet.Boolean) : java.util.Iterator<T> defined in <module>.<root>.kotlin
public final fun <T : jet.Any?>java.lang.Iterable<T>.filter(val predicate : (T) -> jet.Boolean) : java.util.List<T> defined in <module>.<root>.kotlin
public final fun <T : jet.Any?>jet.Iterable<T>.filter(val predicate : (T) -> jet.Boolean) : java.util.List<T> defined in <module>.<root>.kotlin
public final fun <T : jet.Any?>jet.Array<T>.filter(val predicate : (T) -> jet.Boolean) : java.util.List<T> defined in <module>.<root>.kotlin


#11

Something remotely related ... I had a look at what is happening in toSet():

val set = HashSet<Int>()
set.add(1)
set.add(2)
set.add(3)

val selectedSet : Set<Int> = (set.select{ it >= 2 }).toSet()

It goes into this:

public inline fun <in T> java.lang.Iterable<T>.toSet() : Set<T> = to(HashSet<T>())

which calls this:

public inline fun <in T, C: Collection<in T>> java.lang.Iterable<T>.to(result: C) : C {
  for (element in this) result.add(element)
  return result
}

So the toSet() method always copies the elements of the receiver collection into a new set even if the receiver object already is a set and there is hence no conversion to be done. This could be avoided with something like this:

fun <T> Set<T>.toSet() : Set<T>
{
  return this
}

fun <T> Collection<T>.toSet() : Set<T>
{
  return to(HashSet())
}

Just a little thing I wanted to mention.
Cheers, Oliver


#12

The problem here is bad diagnostics.

There actually is an error at the line marked with “1”: you are trying to assign a list of Bar’s to a list of Foo’s, which is not allowed, because the List class is not covariant in its type parameters (Java does not allow you to assign a list of Strings to a list of Objects, and it’s right).

The bad diagnostics are caused by too little intelligence wire into our type inference engine so far. Will improve it soon.


#13

Yeah, and to make it compile, you can say, for example:

``

  val filteredFooList : List<out Foo> = barList.filter{it.getBar() >= 2} // does not compile ?! – 1

“out Foo” is a better version of Java’s “? extends Foo”


#14

Hi Andrey,

thanks for your reply. Think I wrote some fabulous code now that made the compiler break: Compiler terminated with exit code: 2. It is not a compiler error concerning my code, but something inside jetbrains code generation seems to break (judging from the stack trace below). Here is what I did which in this first step still compiles and runs fine:

private inline fun <T> Collection<T>.newInstanceNotNull() : Collection<T>
{
  val newInstance = javaClass.newInstance()
  if(newInstance != null)
  return newInstance
  return ArrayList<T>
}

public inline fun <T> Collection<T>.select(fn : (T) -> Boolean) : Collection<T>
{
  val result = newInstanceNotNull()
  for (item in this) {
  if (fn(item))
           result.add(item)
  }
  return result
}

fun main(args : Array<String>)
{
  val list = ArrayList<Int>()
  list.add(3)
  list.add(7)

  val set = HashSet<Int>()
  set.add(3)
  set.add(7)

  var resultList : List<Int> = (list.select{ it < 12}).toList()
  println(resultList)

  var resultSet : Set<Int> = (set.select{ it < 12}).toSet()
  println(resultSet)
}

Then I thought that this toList and toSet conversion is not too bloody elegenat. So I added these two methods:

public inline fun <T> Set<T>.select(fn : (T) -> Boolean) : Set<T>
{
  return super.select(fn)
}

public inline fun <T> List<T>.select(fn : (T) -> Boolean) : List<T>
{
  return super.select(fn)
}

The test code then becomes:

fun main(args : Array<String>)
{
  val list = ArrayList<Int>()
  list.add(3)
  list.add(7)

  val set = HashSet<Int>()
  set.add(3)
  set.add(7)

  var resultList : List<Int> = list.select{ it < 12}
  println(resultList)

  var resultSet : Set<Int> = set.select{ it < 12}
  println(resultSet)
}

Note, there is no more toList() nor toSet(). When I rebuild the project I get this exception dump (after waiting for quite a while):

java.lang.IllegalStateException: Internal error: (34,18) java.lang.NullPointerException
@BindingContext.java:144
  at org.jetbrains.jet.codegen.CompilationErrorHandler$1.reportException(CompilationErrorHandler.java:27)
  at org.jetbrains.jet.codegen.GenerationState.compileCorrectFiles(GenerationState.java:120)
  at org.jetbrains.jet.compiler.CompileSession.generate(CompileSession.java:161)
  at org.jetbrains.jet.compiler.CompileEnvironment.compileModule(CompileEnvironment.java:156)
  at org.jetbrains.jet.compiler.CompileEnvironment.compileModuleScript(CompileEnvironment.java:107)
  at org.jetbrains.jet.cli.KotlinCompiler.exec(KotlinCompiler.java:144)
  at org.jetbrains.jet.cli.KotlinCompiler.exec(KotlinCompiler.java:90)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
  at java.lang.reflect.Method.invoke(Method.java:597)
  at org.jetbrains.jet.plugin.compiler.JetCompiler.execInProcess(JetCompiler.java:276)
  at org.jetbrains.jet.plugin.compiler.JetCompiler.runInProcess(JetCompiler.java:243)
  at org.jetbrains.jet.plugin.compiler.JetCompiler.doCompile(JetCompiler.java:148)
  at org.jetbrains.jet.plugin.compiler.JetCompiler.compile(JetCompiler.java:104)
  at com.intellij.compiler.impl.CompileDriver.compileSources(CompileDriver.java:1931)
  at com.intellij.compiler.impl.CompileDriver.translate(CompileDriver.java:1254)
  at com.intellij.compiler.impl.CompileDriver.doCompile(CompileDriver.java:986)
  at com.intellij.compiler.impl.CompileDriver.doCompile(CompileDriver.java:747)
  at com.intellij.compiler.impl.CompileDriver.access$1000(CompileDriver.java:104)
  at com.intellij.compiler.impl.CompileDriver$8.run(CompileDriver.java:665)
  at com.intellij.compiler.progress.CompilerTask.run(CompilerTask.java:155)
  at com.intellij.openapi.progress.impl.ProgressManagerImpl$TaskRunnable.run(ProgressManagerImpl.java:469)
  at com.intellij.openapi.progress.impl.ProgressManagerImpl$2.run(ProgressManagerImpl.java:178)
  at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:218)
  at com.intellij.openapi.progress.impl.ProgressManagerImpl.runProcess(ProgressManagerImpl.java:169)
  at com.intellij.openapi.progress.impl.ProgressManagerImpl$8.run(ProgressManagerImpl.java:378)
  at com.intellij.openapi.application.impl.ApplicationImpl$6.run(ApplicationImpl.java:434)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
  at java.util.concurrent.FutureTask.run(FutureTask.java:138)
  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
  at java.lang.Thread.run(Thread.java:662)
  at com.intellij.openapi.application.impl.ApplicationImpl$1$1.run(ApplicationImpl.java:145)
Caused by: Internal error: (34,18) java.lang.NullPointerException
@BindingContext.java:144
  at org.jetbrains.jet.codegen.ExpressionCodegen.genQualified(ExpressionCodegen.java:156)
  at org.jetbrains.jet.codegen.ExpressionCodegen.visitDotQualifiedExpression(ExpressionCodegen.java:1569)
  at org.jetbrains.jet.codegen.ExpressionCodegen.visitDotQualifiedExpression(ExpressionCodegen.java:58)
  at org.jetbrains.jet.lang.psi.JetDotQualifiedExpression.accept(JetDotQualifiedExpression.java:37)
  at org.jetbrains.jet.codegen.ExpressionCodegen.genQualified(ExpressionCodegen.java:147)
  at org.jetbrains.jet.codegen.ExpressionCodegen.gen(ExpressionCodegen.java:161)
  at org.jetbrains.jet.codegen.ExpressionCodegen.gen(ExpressionCodegen.java:165)
  at org.jetbrains.jet.codegen.ExpressionCodegen.visitReturnExpression(ExpressionCodegen.java:925)
  at org.jetbrains.jet.codegen.ExpressionCodegen.visitReturnExpression(ExpressionCodegen.java:58)
  at org.jetbrains.jet.lang.psi.JetReturnExpression.accept(JetReturnExpression.java:38)
  at org.jetbrains.jet.codegen.ExpressionCodegen.genQualified(ExpressionCodegen.java:147)
  at org.jetbrains.jet.codegen.ExpressionCodegen.gen(ExpressionCodegen.java:161)
  at org.jetbrains.jet.codegen.ExpressionCodegen.generateBlock(ExpressionCodegen.java:853)
  at org.jetbrains.jet.codegen.ExpressionCodegen.visitBlockExpression(ExpressionCodegen.java:722)
  at org.jetbrains.jet.codegen.ExpressionCodegen.visitBlockExpression(ExpressionCodegen.java:58)
  at org.jetbrains.jet.lang.psi.JetBlockExpression.accept(JetBlockExpression.java:45)
  at org.jetbrains.jet.codegen.ExpressionCodegen.genQualified(ExpressionCodegen.java:147)
  at org.jetbrains.jet.codegen.ExpressionCodegen.gen(ExpressionCodegen.java:161)
  at org.jetbrains.jet.codegen.ExpressionCodegen.returnExpression(ExpressionCodegen.java:936)
  at org.jetbrains.jet.codegen.FunctionCodegen.generatedMethod(FunctionCodegen.java:232)
  at org.jetbrains.jet.codegen.FunctionCodegen.generateMethod(FunctionCodegen.java:73)
  at org.jetbrains.jet.codegen.FunctionCodegen.gen(FunctionCodegen.java:63)
  at org.jetbrains.jet.codegen.NamespaceCodegen.generate(NamespaceCodegen.java:71)
  at org.jetbrains.jet.codegen.GenerationState.generateNamespace(GenerationState.java:131)
  at org.jetbrains.jet.codegen.GenerationState.compileCorrectFiles(GenerationState.java:114)
  … 33 more
Caused by: java.lang.NullPointerException
  at org.jetbrains.jet.lang.resolve.BindingContext$3.normalize(BindingContext.java:144)
  at org.jetbrains.jet.lang.resolve.BindingContext$3.normalize(BindingContext.java:125)
  at org.jetbrains.jet.util.slicedmap.Slices$SliceWithOpposite.makeKey(Slices.java:176)
  at org.jetbrains.jet.util.slicedmap.SlicedMapImpl.get(SlicedMapImpl.java:85)
  at org.jetbrains.jet.lang.resolve.BindingTraceContext.get(BindingTraceContext.java:79)
  at org.jetbrains.jet.lang.resolve.BindingTraceContext$1.get(BindingTraceContext.java:47)
  at org.jetbrains.jet.codegen.ExpressionCodegen.generateThisOrOuter(ExpressionCodegen.java:1436)
  at org.jetbrains.jet.codegen.StackValue$ThisOuter.put(StackValue.java:1018)
  at org.jetbrains.jet.codegen.StackValue$CallReceiver.genReceiver(StackValue.java:1155)
  at org.jetbrains.jet.codegen.StackValue$CallReceiver.put(StackValue.java:1133)
  at org.jetbrains.jet.codegen.ExpressionCodegen.invokeMethodWithArguments(ExpressionCodegen.java:1353)
  at org.jetbrains.jet.codegen.ExpressionCodegen.invokeFunction(ExpressionCodegen.java:1289)
  at org.jetbrains.jet.codegen.ExpressionCodegen.visitCallExpression(ExpressionCodegen.java:1255)
  at org.jetbrains.jet.codegen.ExpressionCodegen.visitCallExpression(ExpressionCodegen.java:58)
  at org.jetbrains.jet.lang.psi.JetCallExpression.accept(JetCallExpression.java:45)
  at org.jetbrains.jet.codegen.ExpressionCodegen.genQualified(ExpressionCodegen.java:147)
  … 57 more

I was a bit surprised that f.ex.

public inline fun <T> Set<T>.select(fn : (T) -> Boolean) : Set<T>
{
  return super.select(fn)
}

compiled fine. I thought I would have to write something like this to make it compile:

public inline fun <T> Set<T>.select(fn : (T) -> Boolean) : Set<T>
{
  return super.select(fn).toSet()
}

with toSet being:

public inline fun <T> Set<T>.toSet() : Set<T>
{
  return this
}

Anyway, I get the same compiler dump with this approach as well. Maybe someone could spare a little time to have a look into this. Would be nice if the old Smalltalk yourself trick (method returns self as in the method just above this paragraph) could be applied here.

Kind regards, Oliver


#15

Please, file an issue in uor bug tracker. Thanks.


#16

Okay, I just filed the isse: http://youtrack.jetbrains.com/issue/KT-1851

There is something else I wanted to ask, but don’t dare to open up a new topic for. But maybe someone competent will read this. Question is: “How do you pronounce Kotlin?”. I used to pronounce it like “Kootliiin” which is what someone of my mother tounge would say if not be told otherwise. Now I have a co-worker of Russion origin and he pronounced it with a very short “o” and a short “i”. Now I’m confused …