AbstractTraversable.headOption throws NoSuchElementException

110 views
Skip to first unread message

Rado Buranský

unread,
May 6, 2015, 3:25:22 PM5/6/15
to scala-l...@googlegroups.com
The subject is not really correct, but I'd like to know you're opinion on how to avoid this kind of issue:

import scala.collection.JavaConversions._

val files: java.lang.Iterable[...] = ...
files
.headOption match { ... // This throws NoSuchElementException

If you're curious the stack trace looked like this:
...
Caused by: java.util.NoSuchElementException
at com.google.common.collect.AbstractIterator.next(AbstractIterator.java:152)
at scala.collection.convert.Wrappers$JIteratorWrapper.next(Wrappers.scala:42)
at scala.collection.IterableLike$class.head(IterableLike.scala:91)
at scala.collection.AbstractIterable.head(Iterable.scala:54)
at scala.collection.TraversableLike$class.headOption(TraversableLike.scala:436)
at scala.collection.AbstractTraversable.headOption(Traversable.scala:105)
...

The reason is that the implementation of the Iterable can be iterated only once. But it took me a while to find it out. I simply called an API (SonarQube) to get a collection of files and I naturally didn't care about implementation of the Iterable interface. Should I? Who's fault is this and how to avoid it?

Thanks for opinions

Nils Kilden-Pedersen

unread,
May 6, 2015, 3:35:24 PM5/6/15
to scala-l...@googlegroups.com

A java.lang.Iterable should return a valid Iterator when requested. Any implementation that doesn’t do that, is either faulty, or designed that way thus not generally usable.


--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rex Kerr

unread,
May 6, 2015, 3:58:08 PM5/6/15
to scala-l...@googlegroups.com
For what it's worth, there's no reason why headOption should traverse twice.  I think I have a performance optimization patch somewhere in the works that will fix this.  So then _this_ code won't break, but other code that should work will break because this java.lang.Iterable only returns one good Iterator, which isn't really what one expects from an Iterable.  If it's only good once, it should just be an Iterator.

I'm not sure there's any way to catch wonky usages of the API except to try them and see what happens (and submit a bug report to the offending project if it does something weird).

  --Rex

Erik Osheim

unread,
May 6, 2015, 7:20:17 PM5/6/15
to scala-l...@googlegroups.com
On Wed, May 06, 2015 at 12:58:06PM -0700, Rex Kerr wrote:
> I'm not sure there's any way to catch wonky usages of the API except to try
> them and see what happens (and submit a bug report to the offending project
> if it does something weird).

This might be overkill for Scala collections, but I could imagine
publishing a laws package that encodes the invariants for each
collection type, allowing folks to easily test their own custom
implementations against the expectations of the API/library.

For example, I could imagine testing the following properties:

// C[A] <: Iterable[A]
forAll { (xs: C[A]) =>
val lst1 = xs.iterator.toList
val lst2 = xs.iterator.toList
lst1 shouldBe lst2

xs.size shouldBe xs.iterator.size

xs.isEmpty shouldBe xs.iterator.isEmpty

// ...and so on
}

I just thought I'd mention this, since the strategy of exporting laws
to test against has been pretty successful in the "functional" corner
of the Scala ecosystem.

-- Erik

Andrew Phillips

unread,
May 6, 2015, 8:34:36 PM5/6/15
to scala-l...@googlegroups.com, er...@plastic-idolatry.com
> xs.size shouldBe xs.iterator.size 
>
> xs.isEmpty shouldBe xs.iterator.isEmpty 

For related fun, see http://scalapuzzlers.com/#pzzlr-048

Regards

ap

Jason Zaugg

unread,
May 6, 2015, 9:21:24 PM5/6/15
to scala-l...@googlegroups.com
Rex Kerr has been working on something like this for the collections:


So far, our focus has been on using this to test the standard collections, we haven't documented how to use this for your own collections.

Unlike traditional reusable property based tests, this suite uses code generation so that interactions with type inference and implicit search for the relevant CanBuildFrom is tested.

-jason
Reply all
Reply to author
Forward
0 new messages