Skip to content

Add missing IArray operations #11354

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

Merged

Conversation

nicolasstucki
Copy link
Contributor

@nicolasstucki nicolasstucki commented Feb 9, 2021

  • Add missing operations
  • Adapt some existing operations
  • Remove some ClassTag from existing operations that did not need them
  • Added IArrayOps.WithFilter and IArrayBuilder

TODO

  • Solve Cannot fully construct extension method for IArray #11358 and add missing overloads that receive IArrays
  • Fix double definition of startsWith (removed default parameters)
  • Should operations with implicit conversion keep those conversions? See flatMap, WithFilter.flatMap, flatten, unzip, unzip3, transpose.
  • Where should we place the WithFilter and IArrayBuilder?

Left for later

  • Refactor collective extensions and add documentation

@nicolasstucki nicolasstucki self-assigned this Feb 9, 2021
@nicolasstucki nicolasstucki changed the title Add missing i array operations Add missing IArray operations Feb 9, 2021
@nicolasstucki nicolasstucki force-pushed the add-missing-IArray-operations branch 4 times, most recently from e86d821 to 79743c7 Compare February 9, 2021 14:35
@nicolasstucki nicolasstucki added this to the 3.0.0-RC1 milestone Feb 9, 2021
@nicolasstucki nicolasstucki force-pushed the add-missing-IArray-operations branch from 79743c7 to d5dcb7b Compare February 9, 2021 15:14
@nicolasstucki nicolasstucki force-pushed the add-missing-IArray-operations branch 8 times, most recently from fabcb58 to 16b2ed7 Compare February 10, 2021 10:45
@@ -102,7 +100,7 @@ object opaques:

/** Flattens a two-dimensional array by concatenating all its rows
* into a single array. */
extension [T](arr: IArray[T]) def flatten[U: ClassTag](using T => Iterable[U]): IArray[U] =
extension [T](arr: IArray[T]) def flatten[U: ClassTag](using asIterable: T => Iterable[U]): IArray[U] =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are against adding new implicit conversions should we simplify the definitions of flatMap, WithFilter.flatMap, flatten, unzip, unzip3, transpose?

For this one the alternative is to define it as

 extension [T](arr: IArray[Iterable[T]]) def flatten: IArray[T] = ...

We could also add an overload for

 extension [T](arr: IArray[IArray[T]]) def flatten: IArray[T] = ...

@odersky @sjrd WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need an implicit conversion from IArray[T] to Iterable[T] anyway, because I can to be able to give IArrays in the flatMap methods of List and other collections.

Copy link
Contributor Author

@nicolasstucki nicolasstucki Feb 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the conversion be to Iterable[T] or directly to immutable.ArraySeq[T]?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

immutable.ArraySeq sounds reasonable to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allowed me to remove many redundant operations. See the second commit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with using Conversion instances is that you get a feature warning.

there were 1 feature warning(s); re-run with -feature for details

If you run with -feature you get something like this

  |Use of implicit conversion given instance genericWrapAnyRefIArray in object IArray should be enabled
  |by adding the import clause 'import scala.language.implicitConversions'
  |or by setting the compiler option -language:implicitConversions.
  |See the Scala docs for value scala.language.implicitConversions for a discussion
  |why the feature should be explicitly enabled.

To prevent this you can use an old-style implicit def. Or, alternatively, change checkImplicitConversionUseOK to make a special case for conversions coming from IArray. But the second alternative requires a discussion what the policy wrt implicit conversions should be. So I think it's easiest for now to simply revert to implicit defs for the conversions here.

Copy link
Contributor Author

@nicolasstucki nicolasstucki Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean flatMap as in the following example?

for
  x <- List(1, 2)
  y <- IArray(1, 2)
yield ...

For this, we do not need the implicit conversion. Actually it did not work without the implicit conversion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used implicit defs and aligned the implementation with the one for Arrays

@nicolasstucki nicolasstucki force-pushed the add-missing-IArray-operations branch 8 times, most recently from 001b436 to a61aeea Compare February 11, 2021 07:55
@nicolasstucki nicolasstucki force-pushed the add-missing-IArray-operations branch from a61aeea to 85b699e Compare February 11, 2021 08:24
@nicolasstucki nicolasstucki marked this pull request as ready for review February 11, 2021 09:53

/** Conversion from IArray to immutable.ArraySeq */
extension [T](arr: IArray[T]) def toSeq: immutable.ArraySeq[T] =
immutable.ArraySeq.unsafeWrapArray(arr.asInstanceOf[Array[T]])
given genericWrapIArray[T](using DummyImplicit): Conversion[IArray[T], scala.collection.immutable.ArraySeq[T]] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the DummyImplicits, here and below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I don't I get an error at call site

[error] -- [E007] Type Mismatch Error: library/src/scala/runtime/TupleXXL.scala:26:18 
[error] 26 |        while i < es.length do
[error]    |                  ^^
[error]    |Found:    (TupleXXL.this.es : IArray[Object])
[error]    |Required: ?{ length: ? }
[error]    |Note that implicit extension methods cannot be applied because they are ambiguous;
[error]    |both object IArray in package scala and given instance genericWrapIArray in object IArray provide an extension method `length` on (TupleXXL.this.es : IArray[Object])

Maybe they should be placed in Predef as are the ones for Arrays. But adding them to stdLibPatches/Predef.scala crashed the compiler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoided this with implicit def

*/
@SerialVersionUID(3L)
sealed abstract class IArrayBuilder[T]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, now that I see it here, I don't think it's the right place for this one, in fact. ArrayBuilder is a top-level class in scala.collection.mutable, even though Array is in scala.*. By analogy, IArrayBuilder should also be a top-level class in scala.collection.mutable (yes, mutable, like StringBuilder).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to scala.collection.mutable

/** A companion object for array builders.
*/
object IArrayBuilder {
Copy link
Contributor

@odersky odersky Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a way to re-use array builders here. I believe there is an operation where you create a builder by using an existing builder and adding a post-processing step. In this case the post-processing step would be simply asInstanceOf[IArray[T]]. Can we use that one?

Copy link
Contributor

@odersky odersky Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could still have specialized versions for each primitive type, but we would not need to manually re-implement all the methods.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed! This should probably be as easy as

@inline def makeIArrayBuilder[T: ClassTag]: Builder[T, IArray[T]] =
  mutable.ArrayBuilder.make[T].mapResult(r => r: IArray[T])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reimplemeted IArrayBuilder and moved it to scala.collection.mutable.

@nicolasstucki nicolasstucki linked an issue Feb 11, 2021 that may be closed by this pull request
@nicolasstucki nicolasstucki force-pushed the add-missing-IArray-operations branch from c687c65 to b26da40 Compare February 11, 2021 13:32
sjrd
sjrd previously approved these changes Feb 11, 2021
Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM other than the dangerous import, and possibly using mapResult for the builder.

* @tparam T type of the elements for the array builder, with a `ClassTag` context bound.
* @return a new empty array builder.
*/
@inline def make[T: ClassTag]: IArrayBuilder[T] = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it that using mapResult didn't work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works, but the signature must be Builder[T, IArray[T]] and it adds an extra indirection.

This would mean that we do not have the efficient addAll(xs: IArray[T]) and (xs: IArray[T], offset: Int, length: Int) on that builder.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could also have addAll(xs: Array[T])

Copy link
Contributor Author

@nicolasstucki nicolasstucki Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have 2 options:

  • Return IArrayBuilder[T]
    • Larger code
    • Efficient add for IArray/Array
    • Possible to create a specialized IArrayBuilder[T] for primitive types
  • Return Builder[T, IArray[T]]
    • Trivial code
    • In-efficient add for IArray/Array
    • Impossible to create a specialized Builder[T, IArray[T]] for primitive types

I'm inclined to have the IArrayBuilder[T] for performance reasons. Otherwise, we might see applications that use the ArrayBuilder and then cast it to IArray (or use IArray.unsafeFromArray)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Impossible to create a specialized Builder[T, IArray[T]] for primitive types

Hum, that's not the case. If you go through ArrayBuilder.make[T] followed by a mapResult, you will receive the specialized ArrayBuilder, which will create a specialized Array[T], and the only thing is that is going to be cast to an IArray[T] at the end of the day, which even when not specialized is a no-op. So we would definitely have the specialized builders for IArrays.

Now we couldn't have addAll specialized for IArray and Array, that's true. However addAll is virtually never used on Builders.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue was alluding to is that the result of mapResult does not have an overload of addAll that receives Arrays.

But if it is almost never used I will remove this.

@nicolasstucki nicolasstucki force-pushed the add-missing-IArray-operations branch from b6899a5 to 46c0540 Compare February 11, 2021 14:38
These call use `java.lang.System.arraycopy` to add the elements
@nicolasstucki nicolasstucki merged commit ae7bbd8 into scala:master Feb 11, 2021
@nicolasstucki nicolasstucki deleted the add-missing-IArray-operations branch February 11, 2021 22:23
* @param it the iterable collection
* @return an array consisting of elements of the iterable collection
*/
def from[A : ClassTag](it: IterableOnce[A]): Array[A] =
Copy link
Member

@bishabosha bishabosha Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this intended to return an Array?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch. Nope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #11387

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Review/fix definition of IArray
5 participants