Return from insode closure


#1

Hello,

ich have a method that looks like this:

fun foo() : Int {   val list = hashSetOf(1, 2)   list.forEach { x ->            if(true)            return x   }   return 5   }

It does not compile and creates the error "return not allowed here", which refers to the line "return x". Is this the way it is or am I doing something wrong? Am using Kotlin   0.5.75.

Thanks, Oliver


#2

Oliver

A function literal never uses a return.
For example, the successor function would be presented as:

val successor: (Int) -> Int = { x -> 1 + x }

and not as:

val successor: (Int) -> Int = { x -> return 1 + x }

Ken


#3

Here is some Smalltalk code that defines a class Foo with a method bar:

Object subclass: #Foo

bar

  | list value |
  
  list := OrderedCollection new add: 1; add: 2; add: 3; yourself.
  value := list do: [ :each |   
  Transcript show: each printString.
  true ifTrue: [^each].   
  ].
  ^5.


Now I call Foo.bar running this code:

Transcript show: Foo new bar printString.

It prints "11" to the Smalltalk Transcript window as expected. How do I do the same thing in Kotlin? By the way, the Kotlin code below also does not compile. The error message is "break and continue are only allowed inside a loop".

public class Foo {

  fun bar() : Int {
  val set = hashSetOf(1, 2)
  set.forEach { x ->
           if(true)
           break
  }
  return 5
  }
}


Cheers, Oliver


#4

Hi!

This features ( 1 2 ) not implemented yet, but you can watch issues KT-1435 and KT-1436.

If you have any questions feel free to write.


#5

Thanks for the answer. I was told that there is a workaround in Groovy:

def list = [1, 2, 3]
def value = list.findResult { each ->
  println(each)
  if(each)
    return each
  return 5
}
println(value)

The code above then prints "11". Would that kind of workaround be possile in Kotlin as an intermediate solution till Kotlin turns 1.0? I'm starting to be a nuisance, I know ... ;-).

Thanks, Oliver


#6

Workaround in Kotlin (at web-demo):

fun main(args : Array<String>) {   val l = listOf(1, 2, 3)      var result = -1;      l.all {   if (it == 2) {   result = it   false   }   else {   true   }   }      println(result)

  l.any {
  var stop = false

  if (it == 1) {
  result = it
  stop = true
  }
  
  stop
  }
  
  println(result)

}


#7

Another workaround is:

class LocalReturn<T>(val result: T): Exception()

object fakeScope {
  fun ret<T>(result: T): Unit = throw LocalReturn(result);
}

fun scope<T: Any>(f: fakeScope.()->Unit): T? {
  try {
  fakeScope.f()
  } catch (e: LocalReturn<T>) {
  return e.result
  }
  return null
}

fun main(args : Array<String>) {
  val l = listOf(1, 2, 3)
  
  var result = scope<Int> {
  l.forEach {
  if (it == 2) ret(it)
  }
  }
  
  println(result)
}


#8

But you should understand that both solutions may have performance problems.


#9

Thanks Zalim,

what I don’t really understand is why it is possible to do a return from inside the closure provided to the all iterator, but not in case of the forEach iterator. So the issue does not depend on closures? I’m a bit confused here …

Cheers, Oliver


#10

It's implementation characteristics of functions "all" and "forEach". You can see it in documentation (all, forEach) or source (all, forEach)

In a nutshell:
forEach iterates over all the elements
And all stops after the first false result from predicate


#11

Just found this here:

http://dev.bizo.com/2010/01/scala-supports-non-local-returns.html

So Scala implements non-local returns through throwing exceptions as also suggested by bashor in his last post. That is a little disappointing … Maybe it’s a JVM limitation all languages have to live with?

– Oliver


#12

Unfortunately yes -- it's JVM limitation. But I dont sure that the implementation with exceptions(like in Scala) is best solution. It necessary to investigate.