arrayOf unnatural

In my skimming of the majority of this thread, I haven’t noticed any positive opinions expressed yet towards arrayOf syntax, so, even without giving any prescription for what should be done, I’ll throw these pennies of opinion into the statistics pile:

Even though I agree that I’ve felt it annoyingly occupies excess screen real-estate in a language that already forces source code lines to be wrapped too frequently:
I do also feel that there is beauty to be found in the intentionally uniform-looking code that Kotlin is promoting.

For me, the bad side is most commonly recognized in parameter lists, and the good side can be felt when writing standard initialized variable declarations.

Additionally, while I believe that all primitive data types should in fact have dedicated, concise language-defined syntax uniquely specifying literals as that type (creating a short, for example shouldn’t have to be 42.toShort()), perhaps the current lack of such a syntax already existing will become a boon when bringing so-called “value types” to the language. Just a thought.

1 Like

This is why you put that code into a git submodule :slight_smile:

The [ … ] array literal seems to be part of the 1.2 EAP. See Early access program for Kotlin 1.2 has been started | The Kotlin Blog

Although only in annotations, for some reason.

Thanks to the @kotlin team! I usually avoid EAPs but for the addition of (even though limited) array annotation I am making an exception. Thanks again.

Any progress on this feature?
I propose to introduce new operator for collection literals:

operator fun <T> collection(vararg args: T) = listOf(args)

// usage
val list: List<String> = ["a", "b", "c"]

The benefit: can be overridden locally for your needs

honestly, I don’t think this syntax has much benefits over “arrayOf” or “listOf” etc.
the simple bracketed array notation with […] also has some logical issues, because you will still have to provide the actual type of the collection you are instantiating with it, like in your example, you alwasy need the List part (or Array<…>, or MutableList<…> …) in order to decide on the actual type of the collection. Simple

val array = [a,b,c]

won’t work, because [a,b,c] could be an Array, a List, a MutableList, a Set… unless you define on the language level that is always an Array, for example, and so you still would need a listOf([a,b,c]) Syntax…

Collection literals (outside of annotations) currently seem to be stuck on nothing implemented because there’s no consensus on a perfect solution that covers all requirements.

Perhaps it would be best to accept that a generic solution for collection literals will never happen, and just implement array literals in regular code the same as in annotations. Nothing is lost, the syntax is consistent with annotations, and at least the lowest hanging fruit is picked.

So basically “We can’t have what we want so let’s add something inferior and call it done”. I don’t think this is a good design philosophy for a language. At least not if you want the language to stay relevant for the next 10 years or longer.
Arrays are a less powerful structure to List since they don’t have a read only version. There are very few situations where a pure array is supperior to a list so I don’t like the idea of promoting arrays over lists by giving them a special syntax.

2 Likes

Yes, when the options are to solve none or just some of the requirements, I do think that an imperfect solution is preferred. It’s called pragmatic :slight_smile:

The reason for choosing Arrays over List is the same as vararg - it’s the simplest data type which represents the input. If you consider where collection literals are used in practice (arrayOf, listOf, etc.), it’s usually in short scripts, or sample data in test code. In either case, all you need is something which can be iterated over. Unless you need to provide a specific collection type/interface for the API under test, in which case your code is clearer with an explicit constructor.

Another way of looking at it is that collection literals are most useful in circumstances where the type of the collection doesn’t actually matter.

Hmm, that got me thinking: could you define that the desired type is inferred from the context (e.g., variable declaration, function parameter type, etc.) and, if immutable, a given specified mutable collection subtype is constructed? There could even be some way to extend the set of definitions within a given scope, maybe using annotations.

That might violate the principle of least surprise, though. Personally I think the trick of “object with operator fun get” is pretty good - or just use a straight function if you’re happy to have round brackets after your single letter (e.g., those of you using a kezboard ;-). I suppose the underlying collection constructor probably isn’t evaluated at compile time so it isn’t really a literal, but it would do in most cases.

No. If the context says Set<MyEnum>, you still don’t know if you should construct a LinkedHashSet, TreeSet, EnumSet, Guava ImmutableSet or whatever, and the ways to construct these sets also differ. There’s not always a constructor available.

Java9 got it right: List.of(…), Set.of(…), etc.

1 Like

Yes, I understand that. Perhaps I wasn’t clear; that’s why I said

Unless you take the routes of “don’t add this” or “just hard-code one concrete type of list and map”, the nub of the problem is that some people want a syntax which is terse enough to construct a collection without writing out anything about the type, and some people want the constructed type to be suitable in all cases (which implies controlling the constructed type, on the reasonable assumption that you can never have a collection class which satisfies all use cases). To put it more simply, the collection type must be both specified and not specified.

(The reason the problem is solved for annotation arguments, I suppose, is because you can effectively just hard-code one type in that case.)

Taking a leaf from one of my favourite problem-solving toolkits, TRIZ, you solve a “contradiction in terms” (which TRIZ calls a “physical contradiction”) by separating your “specified” and “not specified” into different contexts. The obvious way to apply that high-level guidance here is that you would not specify the type where you use the collection expression (like [1, 2, 3]) but you would specify it somewhere else.

TRIZ then leaves it up to you to work out what “somewhere else” means in your case. IMHO a Kotlin-esque way to do this would

  • use one or more existing pieces/forms of information from existing concepts of scope in the language;
  • be configurable in individual code bases, without causing problems when those pieces are combined (like the amazing framework for coroutines!);
  • work cross-platform; and
  • not fall foul of the principle of least surprise (as long as you read the docs ;-).

Part of one possible solution could be to use declared type info from the variable, property, argument position or other context in which the expression exists, and use that to look up a function (constructor or not) which can initialise a concrete instance from, say, the expression values passed as a vararg array. The lookup might be controlled by annotations in some or all of the usual Kotlin places.

I haven’t thought that through (and won’t have time to) but it might point others in a useful direction. It might be that using type information is too hard and there should just be one default type applicable in the current context. It might be that annotations aren’t a usable way to configure such a mechanism. I haven’t thought of how to make sure it’s not surprising: looking at some source code in a good IDE, how does the developer know the concrete type?

Hope that helps (or at least clarifies what I meant ;-).

1 Like

(Also, every time I see this thread pop up, I find myself wondering what the definition of the unnatural type looks like ;-))

1 Like

It would work. Because user decides on his own what exactly operator will return and how it instantiate a collection.
for example if we use lists instantiation a lot in some class we could define locally scoped operator:

operator fun <T> collection(vararg args: T): List<T> = listOf(args)

Probably this idea should be discussed somewhere in ‘new operators’ topic though

So your arguing that there should be the possiblity to define the operator, but no library should define those publicly. Everyone will just create their own implementation of the operator and they use listOf etc internaly?
Not sure I like that. This will just lead to confusion about what the operator actualy creates in the end. Imagine switching between projects or even files and the same operator having a hole new meaning.

1 Like

I totally agree. Kotlin’s array syntax is unnatural and totally awkward.
A simple example of a matrix declaration:

Kotlin:

val mat1 = arrayOf(intArrayOf(1, 2, 3), intArrayOf(4, 5, 6), intArrayOf(7, 8, 9))
val mat2 = Array(m) {IntArray(n)}

Java (and any other existing language with very minor syntax tweaks):

int[][] mat1 = {{1,2,3}, {4,5,6}, {7,8,9}};
int[][] mat2 = new int[m][n];
2 Likes

While some will agree with you, others won’t. We should all avoid petitioning for our position with a duplicate argument already present in the topic.

In this case, “I like/dislike it” is not new and doesn’t address the more complex pros/cons of the issue.
A few more complex aspects of the topic that might be relevant (at least the ones off the top of my head):

  1. The issue with syntax and other possible uses (e.g. more than just array collection literals, type-classes or other features that could use the syntax)
  2. Alternatives that solve the issue (array builders, etc)
  3. How impactful or widespread the pain on lacking literal array syntax is or is not.

EDIT:
Here’s an example of my personal stance on the topic using the form of addressing these issues:

Number #1 asks if there is a better way to use the syntax. Even if it’s just handling literal collections (not just a literal Array but a literal Set, List, Map, or maybe any arbitrary collection). It’s worth playing with some of these ideas before giving up a valuable syntax forever just because we’re familiar with the Array literal.

Number #2 and #3 asks if this is something we need to rush or pay a high cost to solve the problem. Can we ease the pain of lacking array literals via some library/stdlib change or another new good-enough alternative-- maybe even the existing alternatives?

Number #3 also considers if the pain from declaring literal arrays is large or a common case. I’d suggest that it is not common and the pain is minimal. Other languages like Groovy and Python build their APIs heavily on collection manipulation, but with Kotlin it’s rare to use an Array and even rarer to manually fill an array with elements at declaration.

1 Like

The issue is that Kotlin’s syntax for arrays offers no benefit at all over what it has been established across all languages as the best way to visualize and represent an array or a matrix, which is to use brackets or parenthesis.

Some languages use parenthesis, other brackets, yet the syntax is nearly the same in absolutely all languages, except Kotlin.

What is the benefit of having so many arrayOf, intArrayOf, booleanArrayOf, arrayOfNulls, and a very long etc of array-wording variants?
Would be better to keep it simple with brackets or parenthesis, as absolutely all other languages do for clarity.

Funny enough, if you print in Kotlin the contents of an array into the console by using contentToString or contentDeepToString, the array will be printed with the actual syntax that would had been the preferable one for declaration. Why? Because it is easier to read.

1 Like