Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy enumerators wraps single element arrays into array #5745

Open
ilikeorangutans opened this issue May 24, 2019 · 3 comments

Comments

Projects
None yet
4 participants
@ilikeorangutans
Copy link

commented May 24, 2019

Environment

Provide at least:

  • jruby 9.2.6.0 (2.5.3) 2019-02-11 15ba00b Java HotSpot(TM) 64-Bit Server VM 25.131-b11 on 1.8.0_131-b11 +jit [darwin-x86_64]
  • Darwin Jakobs-MacBook-Pro-2.local 18.6.0 Darwin Kernel Version 18.6.0: Thu Apr 25 23:16:27 PDT 2019; root:xnu-4903.261.4~2/RELEASE_X86_64 x86_64

Expected Behavior

.map { |x| x } should act like the identity function:

a = [[1], [2]]
a.lazy.map { |x| x}.to_a
=> [[1], [2]]

Actual Behavior

But for single array elements it appears to be wrapping elements in an additional array (notice the extra square brackets around the individual elements):

a = [[1], [2]]
a.lazy.map { |x| x}.to_a
=> [[[1]], [[2]]]

However, for arrays with more elements, it works as expected:

a = [[1, 2], [3, 4]]
a.lazy.map { |x| x}.to_a
=> [[1, 2], [3, 4]]

Mixing one element arrays and larger ones leads to the incorrect behaviour for the single element arrays:

a = [[1, 2], [3]]
a.lazy.map { |x| x}.to_a
=> [[1, 2], [[3]]]

Surprisingly, empty arrays are also treated correctly:

a = [[1, 2], []]
a.lazy.map { |x| x}.to_a
=> [[1, 2], []]

Edit:
@lopex suggested this might be due to packed arrays but a quick test with packed arrays disabled (via jruby -Xpacked.arrays=false) exhibited the same behaviour.

@lopex

This comment has been minimized.

Copy link
Member

commented May 24, 2019

Yeah, apparently packed arrays are not the case and surprisingly flat_map matches mri for this case, so I guess we can be fairly sure it's an Enumerator/Lazy combination.

@headius

This comment has been minimized.

Copy link
Member

commented May 28, 2019

This is almost certainly due to ongoing behavioral differences in block argument passing between CRuby's native implementations and the Ruby implementations that we and other implementations prefer. As far back as Rubinius we have seen that specialized logic is required to handle single-argument blocks within an Enumerable/Enumerator – logic that can't be represented with standard Ruby syntax.

Rather than following the Rubinius route of having special "primitive" methods that unpack the arguments properly, I'm wondering if we could just have some special syntax we can use for these internal Ruby core methods that need this behavior. cc @enebo

@enebo

This comment has been minimized.

Copy link
Member

commented May 29, 2019

@headius I have been wondering about extending our "Ruby" for a while now. For internal "kernel" ruby we can not only change semantics of how arguments may be passed but to also essentially add some notion of static dispatch since as impl methods we do not want them overridable and definitely don't want to use refinements to solve that.

In fact, static dispatch is how I would solve this particular problem. It would in essence become a little more of a primitive-like solution as we would have some static call to a method in Java which implements the logic we want; but it would be generic so we could use static calls to solve other things.

My solution to this is to make .jrb but to not ever endorse it as public syntax. This obviously will get used anyways but my intention would be to solve some issues we have in our implementation. Also it would allow some really nice AOT compiled code. We could even lock the syntax somehow so you would need to explicitly call something to make it usable as an additional guard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.