I guess this is expected behavior, but it surprise...
# getting-started
c
I guess this is expected behavior, but it surprised me:
Copy code
val found = sequenceOf(1, 2, 3, 4, 5)
    .filter { it % 2 == 0 }
    .onEach { println("Remaining: $it") }
    .any()
        
println(found)
I was expecting
Remaining: 2
to appear, but no, the
onEach
is never called. It seems that
any
is able to know that there will be other elements without actually processing them. Playground
๐Ÿคฏ 3
j
> It seems that any is able to know that there will be other elements without actually processing them Yes, this is because you hardcode the sequence as a fixed-length sequence. If you replace
sequenceOf()
by a
sequence { ... }
builder, it will print
Remaining: 2
(EDIT: wrong) https://pl.kotl.in/3iXcnj8sh
c
No, it's because it's
any
. Replace
any
by
first
in my example, and you will see that the
onEach
prints.
j
Well, I wouldn't say no. It's because it's
any
AND because it's a fixed-length sequence (EDIT: wrong)
c
(and thanks to the
filter
, my sequence is not fixed-length)
Replace
first
by
any
in your example, and you will see it doesn't print either
j
Oh sorry I thought I had. I tried with both
first
and
any
in your initial code, before trying the dynamic sequence, and I forgot to revert back to
any
in my attempt ๐Ÿ˜…
c
It's because
any
is implemented with
iterator().hasNext()
, and the iterator returned by
onEach
just delegates its
hasNext
to the previous layer, thus
any
is able to ask the filter directly if there are any elements, without actually processing them
๐Ÿ‘ 1
j
Yep that makes sense
It's still surprising, but not too far-fetched
c
Still, I was expecting
any
and
first
to have the exact same side effects
โž• 1
j
I can understand that it's not the case, though. The intent is somewhat different between
any()
and
first()
.
any()
is more like asking a question about the state of the sequence, not really about processing items, which is the case for
first()
.
y
I think it's because
first
needs to actually get an item out, while
any
merely needs to know that one is available. Theoretically, you could have a (weird) onEach implementation that runs its block in
hasNext()
and not
next()
, but that'd be pointless in normal cases except in this example
๐Ÿ‘Œ 3
j
> I would expect this ... To be semantically the same as ... No,
onEach
should only occur on items that are not filtered out, you can't run the
onEach
code inside the filter lambda, it's not equivalent
๐Ÿ™ˆ 1
y
You can imagine that a filter + map maybe shouldn't produce an element if one is not required, and so
any()
should only need to know if the filter succeeds, and it doesn't care that the mapping should actually take place and produce a result (although maybe there's an issue here with exceptions)
In fact, this code demonstrates this;
Copy code
fun main() {
    val found = sequenceOf(1, 2, 3, 4, 5)
        .filter { it % 2 == 0 }
        .map { TODO() }
        .onEach { println("Remaining: $it") }
        .any()
      
    println(found)
}
This prints
true
, and I think I agree with that result, although I can see an argument that it should result in an exception instead. I think due to the laziness of sequences it makes sense (in a Haskell-y way) that a sequence doesn't care about side effects until they absolutely have to run.
k
I found it interesting that it's impossible to do the equivalent in Java:
Copy code
var found = IntStream.of(1, 2, 3, 4, 5)
        .filter(n -> n % 2 == 0)
        .peek(n -> System.out.printf("Remaining: %s%n", n))
        .anyMatch(x -> true);
This prints "Remaining: 2" because the element has to be examined and passed to the predicate of
anyMatch
. Java doesn't have the equivalent of Kotlin's
any()
.
y
Maybe the
Sequence.any
documentation should have a note that
any()
is not equivalent to
any { true }
because of side effects and exceptions
๐Ÿ’ฏ 2
j
That's a very good point. While Java doesn't have the equivalent for Kotlin's
any()
, Kotlin does have an equivalent to Java's
anyMatch(e -> true)
(and that is
any { true }
). So you're right I think it should be clarified in the docs that
any()
is not the same as
any { true }
regarding side effects
s
This is fascinating, I can't decide whether I think it's a bug or not ๐Ÿ˜ฎ. The documentation is light on details, but it categorises sequence operations into only two groups, terminal and non-terminal, and so until reading this I would have assumed that all terminal operations had comparable behaviour...
โž• 2
j
@CLOVIS I think you should consider opening an issue on YouTrack for the Kotlin team, so at least they can clarify whether this is considered expected behaviour or a bug. And if it's expected, update the docs accordingly
๐Ÿ‘ 2
c
๐Ÿ‘ 2