Public review of the Standard Library APIs [Closed]

I’ve also spotted this inconvenience when going through advent of code.
To mitigate this issue we’re going to provide a property MatchResult.groupValues. It would differ from groups in following:

  • returns a list of strings. This list is a wrapper around groups collection.
  • does not include zeroth group value corresponding to the entire match, so that groupValues[0] returns the value of the first group in regex.
  • if some group was not matched the corresponding value in groupValues would be an empty string (“”).

Another solution that comes to mind: Do away with nullable groups, make MatchResult.value return empty string in case of a non-match (or, perhaps better, throw an exception), and add a boolean method MatchResult.isMatch.

So result.groups[3]!!.value == result.groupValues[2]? Sounds like asking for trouble to me (very similar looks/names, incompatible indices).

I would like an assertion function for equal doubles (the one with additional tolerance parameter) to be added to kotlin-test. I have to use JUnit’s Assert.assertEquals instead of Kotlin’s assertions for this. This introduces a bit of inconsistency into tests:

// Need to import org.junit.Assert for this
assertEquals(corner.angle, Math.PI/2, 1e-10)

Another assertion function I’ve missed is assertSame.

1 Like

The split method is not as handy as the Java counterpart, which accepts a plain string instead of a compiled Regex or Pattern. The existing split methods are especially confusing, because the Java counterpart has the same name and almost same signature.

I suggest to change the signature to fun split( regex: String, limit: Int = 0) or to add such a variant.

Java’s way of having some methods treat strings as plain strings, and other methods treat strings as regular expressions, is an anti-pattern. That’s probably why Kotlin uses distinct types. This clearly expresses the difference in the type system, avoiding bugs and puzzlers like “what’s the difference between String.replace(CharSequence, CharSequence) and String.replaceAll(String, String)”, which many Java devs get wrong.

The changed behavior of split() is very much intentional. Using a regex to split has non-trivial performance costs and surprising behavior in cases when a symbol you want to split with has special semantics in a regexp and needs to be escaped. We have explicitly separated methods which take a simple String (and don’t do any fancy regexp matching) and which take a Regex (which could be pre-compiled and stored as a constant).

Utils

These would be nice to have:

Throwable.rootCause() (as in Guava)

Throwable.stackTraceAsString() (as in Guava)

As I happen to just have stated in the Slack channel: I’m a bit dual about ThreadLocal. The property delegate is easily created but any (required) cleanup is easily forgotten, thus adding something like ThreadLocal delegates to the stdlib also introduces the risk of being used while not fully understood, hence leading to potential memory leaks.

That cleaning up also means you need to have a reference to the delegate itself in order to call some remove function. Or allow nulls to null the value out but that won’t work with vals and defies the purpose of the type system.

I disagree strongly that it is more logical. Conceptually, an array or list is like a map in that there is some value you use to refer to the values in the collection and then there are the values in the collection. So the index is analogous to the key in a map. I expect it to be index, value just as when iterating over entries in a map I expect it to be key, value and not the reverse.

Good points. Probably better not added to stdlib (and removed from my wish list).

Groovy and Scala have withIndex methods, which put the index last. Ceylon and Kotlin have indexed methods, which put the index first. Looking at it now, both approaches make sense.

Did anything come out of this, JetBrains? Most of all, I constantly find myself in need of a variant of apply whose function takes a parameter rather than being an extension function. I have my own applyIt, but such a fundamental primitive should be in stdlib.

apply can be used as more than an object initializer. I, for one, use it in unit tests to avoid giving test data trivial names. Compare:

val rectangle = Rectangle(1, 2, 3, 4)
assert(rectangle.contains(Point(1,2))

and

Rectangle(1,2,3,4)
    .apply { assert(contains(Point(1,2)) }
1 Like

We are not renaming any of existing scope functions, but we may provide later such variant of apply. The name of that function is discussable. I’ve reopened issue KT-6903 to track it.

@ilya.gorbunov That’s great to hear. What about the duplication between with and T.run, which are completely equivalent, except that one is a function and the other an Any? extension method? I’d prefer to write foo.with { ... }, as this name, which is also used in other languages, resonates much more with me than foo.run { ... }.

I’ve no idea where to put this though it is now Jan 19.

The documentation of “return” is wrong, see:

https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/generated/_Ranges.kt#L1089

and also the produced doc:

Should be:

@return this value if it’s less than or equal to the [maximumValue] or the [maximumValue] otherwise.

1 Like

After some discussion, we have decided not to drop zeroth group from the groupValues list, so that the same index value would refer to the same group both in groups and in groupValues.

The downside of this decision is that the groupValues list becomes less convenient for destructuring assignment. An intended way to destructure no longer works:
val (group1, group2) = match.groupValues, because the first value now is the match value itself and not the first group value.

In order to make destructuring assignment possible, we’re introducing another property MatchResult.destructured, which only provides component1, component2component10 methods to satisfy the destructuring convention. Thus the usage would be:
val (group1, group2) = match.destructured

Thanks everyone! Now this call is closed