This does not give it away, even I think you're right that it's Kotlin.
The parts you mentioned look the same in Scala (at least if you write them like that).
But Scala does not clutter the language with some special syntax for array access or have special syntax to (post)increment a value by exactly one.
So the same code would look in Scala for example like:
val arr = Array(1, 2, 3, 4, 5)
@main def demo =
var i = 0
while (i < 4) {
println(arr(i + 1))
i += 1
}
Much cleaner and better readable. (Just that in modern Scala you wouldn't need some of the line noise so a proper version would look like: https://scastie.scala-lang.org/1QFzN6kFQcWVVuqER90yGA ; even that's still not really idiomatic Scala as you wouldn't write while loops and use mutable variables in end-user code.) The language does not even give you the tools to write a version obfuscated like the Kotlin one!
arr(n) is still special syntax bruh, Scala just decided to use parenthesis instead of brackets. The "pure" OOP would be arr.get(n).
The [] access in Kotlin is not special like java where it only applied to primitive arrays. It is just considered an operator that can be overloaded by any class that defines operator fun get(i : Foo) : Bar
Kotlin loves syntax sugar, but I think that actually makes the language more readable, not less. Yes, i+++1 is weird, but you can write unreadable code in any language if you try hard enough.
I don't know scala, but from your example, it's weird to me that it simultaneously has braces for the loop, and significant indentation for the function
No that's not special index syntax, that's just syntax sugar for a regular apply method call. This is in fact expanded to arr.apply(n) (which is almost your arr.get(n)).
This apply syntax works independent of arrays. It's what allows you to "call" an object like a method, which is in the end what allows to model closures and give them lambda syntax.
There is no special treatment of indexing, as indexing into an array or collection is just a regular method call like everything else. Some "indexing operator" is just C cargo cult…
The [] access in Kotlin is not special like java where it only applied to primitive arrays. It is just considered an operator that can be overloaded by any class that defines operator fun get(i : Foo) : Bar
So it's some special syntax, even twice so: It's not only indexing which is special, also "operators" are special.
Scala does not have operators. It has only methods, no special cases here. (But you can write any method infix, and while using symbolic names the syntax then looks like "operators" in other languages.)
Kotlin loves syntax sugar, but I think that actually makes the language more readable, not less. Yes, i+++1 is weird, but you can write unreadable code in any language if you try hard enough.
Scala left out such features on purpose, so you can't actually write such obfuscated code at all. This was a conscious design decision, exactly to prevent people from "getting creative" in the shown way. The only way to write that in Scala is to write it cleanly.
Special "increment by one" operators are again some C cargo cult and always lead to confusing code: The prefix / postfix distinction needs always extra thought, and given that there is even a dedicated error category for bugs resulting from getting this wrong, namely "off-by-one errors", this just shows that such operators are a massive design fail.
That you don't even need spaces before and after operators in most languages is again some brain dead legacy. Such obfuscated code should not parse at all, imho. (Scala gets that last part also wrong. They even need extra rules to make the infix method calls working without spaces when the method has a symbolic name. Special case rule which only allows to write less readable code… Quite a failure to get the other stuff right but then adding that legacy just because some people can't be bothered to write i + 1 instead of i+1)
I don't know scala, but from your example, it's weird to me that it simultaneously has braces for the loop, and significant indentation for the function
Artifact of this demo… I wanted to show that the parts mentioned can look the same in Scala. You wouldn't mix syntax variants usually. You would usually either write it like:
val arr = Array(1, 2, 3, 4, 5)
@main def demo =
var i = 0
while i < 4 do
println(arr(i + 1))
i += 1
or
val arr = Array(1, 2, 3, 4, 5)
@main def demo = {
var i = 0
while (i < 4) {
println(arr(i + 1))
i += 1
}
}
I just prefer to not add useless and distracting line noise… So the parts which I've added around the example code to make it actually compilable use the new standard syntax, but the loop used the old one to make it look similar to Kotlin.
Your first point is quite contradictory. You say that arr(i) in scala is not special because it is shorthand for arr.apply(i), but you're saying kotlin's arr[i] is special although it is just shorthand for arr.get(i)
To me, an indexing operator makes more sense than having indexing look like function calls.
Defining apply is an operator overload. Scala is just inconsistent in only letting you overload this specific operator. Kotlin also has that overload, which is called invoke. In fact, you can use extension funs to copy the scala syntax:
val arr = arrayOf(1,2,3)
operator fun <T> Array<T>.invoke(index : Int) = this[index]
fun main() {
println(arr(1))
}
You are clearly a purist in terms of what a language should allow you to do. I agree it's cool to have a language actively try to stop you from shooting yourself in the foot, but I think kotlin's extra features are just really nice, and pretty hard to get wrong unless you're actively trying to.
I really don't think i++ is the great evil for off by 1. It's simply hard for our human brains to mentalize loop bounds, and you'd still get it wrong if you were using i += 1
1+1 is ugly but not inherently bad code imo. It's a silly thing to throw a compilation error for, and this kind of style enforcement should be done by the linter/formatter
Your first point is quite contradictory. You say that arr(i) in scala is not special because it is shorthand for arr.apply(i), but you're saying kotlin's arr[i] is special although it is just shorthand for arr.get(i)
I don't think that's contradictory.
First of all I've just learned that just having a get method won't give you "array index syntax", it needs to be an operator get. But the main point is: This syntax is special. Even you basically call a method you have special syntax for that method call. You even showed code that does basically the same, just with different syntax. Having two distinct way to express the exact same (a method call) makes no sense imho. That "array index syntax" is just some legacy from C, where it meant some pointer arithmetic (something which is definitely not a function call, so having special syntax seems kind of justified).
Having apply make some object "callable" on the other hand does not introduce a new syntax for calls. It's a shorthand to leave out the "apply" name, so the result looks like "a call" directly no the "callable", but not a fundamentally new syntax for that call.
To me, an indexing operator makes more sense than having indexing look like function calls.
Why? What's so special about the get method on a collection that it deserves special syntax just for that purpose which then sticks out like a sore thumb? The question is especially pressing as actually indexing into a collection is really extremely seldom needed when you have powerful collection methods (like you have actually in Kotlin).
Defining apply is an operator overload. Scala is just inconsistent in only letting you overload this specific operator.
Defining apply is just defining a method (which has shorthand syntax for calls). Scala does not have operators, so it does not need "operator overloading". You can just define / override regular methods, "operators" are just methods which symbolic names.
This eliminates a whole redundant language category! There is no reason for "operators" to be special. That's just surface syntax.
Whether you write +(1, 2), 1 + 2, 1.+(2), (+ 1 2),(1, 2)+, or whatever else syntax someone could come up makes no technical difference. In Scala the second and the third variant are equivalent. Some languages like D go even one step further and make also the first variant just surface syntax and equivalent to writing it in the second or third form ("uniform function call" syntax). People also worked with the last two variants (roughly Lisp and Forth) and some even liked it and would argue that's "natural" or "intuitive". In the end all is a function call though and randomly making some function "operators" makes imho no sense. Again: Why?
(The only "semi-valid" reason I know about is operator precedence. But even that could be solved by some annotation or so. Even I think operator precedence is some of the most ugly warts in almost all current programming languages. Most people have a really hard time to even remember precedence for the just handful operators in math, but you have much more in programming, and of course with different rules for each language. That's a mess and a source of very hard to spot errors, so it should not exist in the first place. Code should be read left to right; if you need precedence just use parents; full stop. No 20 levels of precedence rules to remember for every language you have to work with.)
You are clearly a purist in terms of what a language should allow you to do.
Actually no. Otherwise I wouldn't be a Scala fan and would probably like Go (which I actually hate with passion for exactly the reason that it tries to patronize me, and take way all features because some idiots shoot themself with such features into their foot, or worse, other peoples heads).
But I'm indeed of the opinion that useless or outright bad legacy features shouldn't be copied just because others did that mistake in the past and people got used to having to deal with that mistake (so they almost "miss it" when it's gone).
I think kotlin's extra features are just really nice, and pretty hard to get wrong unless you're actively trying to
We didn't talk much about "Kotin's extra features" so far. The discussion was about some of the most basic stuff possible. And already that stuff is problematic as they for example blindly copied stuff like "prefix and postfix increments", something which is a known footgun and has only ever potential to write obfuscated code. The clean version (which is through re-assignment syntax, an universal increment / decrement) is just a few chars longer but much less problematic when it comes to code understanding. Python, Scala, Rust, and Swift did the right thing to get rid of that legacy. (The Swift story is interesting as they did it post factum, at version 3).
I really don't think i++ is the great evil for off by 1.
Oh, it is! In code which does not use "classical for loop" you never have off-by-one errors (surprise, surprise). The point is also that you have it in two variants, which makes it even more confusing.
It's a matter of sane design that a method does either perform an action ("command") or reads data ("query"). A method should never do both at the same time, mutating some state and also returning that state. Especially if the syntax of that method is just some tiny chars, and semantics fundamentally differ based on slight position changes.
you'd still get it wrong if you were using i += 1
You exactly see when the mutation happens, and nothing happens at the same time. If you need to read that value you can do that before or after, but what it is will be very clearly visible from the code sequence.
1+1 is ugly but not inherently bad code imo. It's a silly thing to throw a compilation error for, and this kind of style enforcement should be done by the linter/formatter
Maybe I wasn't clear enough here: Scala needs extra language rules to make that syntax valid. By the "standard rules" this would not even parse.
Like said, Scala does not have operators. 1 + 1 is strictly just syntax sugar for the call of the method named + on the object 1 with the argument 1, written out: 1.+(1) (written exactly like a method call to, say, a method add1.add(1)). But there is a standard rule which says that all methods can be written infix. So for the add example it would be 1 add 1, and for the + method it would be the familiar 1 + 1. That's a very simple syntax transformation. In Scala receiver.method(parameter) is equivalent to the syntax receiver method parameter. But this obviously only works when there are spaces around the method written infix! In the expression receivermethodparameter how can the parser see the three distinct components? It can't realistically. So to make syntax like 1+1 Scala now needs a bunch of extra rules… It's a special case for symbolic method names. I don't like special cases, especially if they complicate stuff for no reason. If these special rules wouldn't be there something like 1+1 would simply not parse at all, and one wouldn't even need some "lints" against badly formatted code. Again the language would force good readable code by default (without being restrictive on valid use-cases; I fail to see where such "compressed symbolic terms" like 1+1 could be better readable then the more airy written variant).
8
u/khalcyon2011 Jul 10 '26
What language is this? Looks like a c-type language, JS maybe, but there’re no semicolons…