When asking Gemini why I couldn't access map keys ...
# getting-started
u
When asking Gemini why I couldn't access map keys with dot notation since they're so similar to objects, it told me that this notation is reserved for values that are known when the code is compiled. In contrast, subscript syntax is reserved for data that's only known as the program runs. Is this an accurate explanation?
y
Yeah, that's a pretty reasonable way to talk about it. Map access (using
map["foo"]
) is for runtime keys, while properties are known at compile time.
u
Thanks for the confirmation!
k
How would that look like?
array.index
? Like
array.0
and
array.1
? And then
array.i
inside the loop? And then how would you express it if it's a more complex computation? It'll just get messy if you have to split
array[functionCall(a,b,c)]
into a separate local variable to use the dot notation - for no good reason.
u
A very good point. I also think that it would be a mess to apply the concept to any collection. However, because of maps' structural similarity to objects, I think dot notation for at least key access would feel pretty natural. Map.key[index] feels good to me, but then I'm also a total beginner.
y
You might wanna look into Kotlin DataFrame! It blurs that line between compile-time and runtime, and makes it really nice to work with structured data
k
As for syntax - dot vs arrow vs colon vs parenthesis vs something else... That's subjective and historical. Kotlin builds on top of Java and very much wants to feel familiar to developers coming from that language. So it's not going to reinvent a bunch of basic things that Java was already doing. And Java was just another language in a long line that borrowed concepts and syntax from each other.
y
Surprisingly, Kotlin and Haskell match in this aspect! Lambdas use arrows, and functions use equals. In general, this syntax is somewhat used in mathematics, so that's likely how it got borrowed over
k
Certainly if your goal as a language designer is to create something that a lot of people adopt, you'd want to find the balance between doing something fresh (because otherwise why are you making a new language) and something familiar (because otherwise it's going to alienate potential developer customers).
u
This is all fantastic context. As I'm trying to wrap my head around everything I learn, I really want to anchor every single piece in an objective justification so that I can make sure I use the pieces together correctly in every situation. But you're absolutely right. In some cases, I simply have to accept things are the way they are just because. As I get more familiar with the language, I'm sure the exceptions will feel less mysterious. I just have to make sure I know the difference between concepts like that and ones I simply don't understand yet, ha. Thank you!
c
Another way to blur the limit:
Copy code
class Foo(val a: Map<String, Any?>) {
    val banana: Int by a
}

val foo = Foo(mapOf("banana" to 5))

println(foo.a["banana"]) // 5
println(foo.banana) // 5
https://kt.academy/article/ak-map-delegate
l
This discussion is more about programming language design in general, and when during the parsing phase you evaluate expressions. I make this note, because my current project is a programing language interpreter, and these exact issues are things that I had to wrestle with.