On the Kotlin survey: I'm really not a fan of intr...
# announcements
c
On the Kotlin survey: I'm really not a fan of introducing
static
to the language. The semantic relationship between classes and objects are a beautiful way to remove complexity to the language while adding new features. To my understanding, the proposal exists for two reasons: 1. For performance reasons 2. To allow extension functions on classes that do not have a companion object I don't think it's a good idea to introduce
static
for performance reasons (the language should be about semantics, the compiler should handle performance). If it's not possible to do automatically by the compiler (because it would break backward-compatibility, or whatever) then at most it should be a JVM-specific annotation. I don't think a new keyword is necessary for classes that do not have a companion object. I think Kotlin should allow:
fun Foo.Companion.bar() = ...
even if Foo doesn't have a Companion. Obviously the compiler will have to replace the companion by something else, but that's not really a big deal (it will be invisible in Kotlin code, and extension functions are already a bit weird from Java code, so as long as it's not something extremely strange I think it's alright)
e
then the meaning of
this
within the extension function changes depending on whether the target class has a companion or not at the time of compile? having a fake dangling reference like that seems prone to other consistency issues
c
My understanding of namespaces is that it's just a compiler thing that makes a ‘simile-companion' on-the-fly?
n
I'm just curious, why does this problem of giving types we don't control "static-like" functions, even need to be solved?
e
Companion classes are more than just namespaces, they are singleton objects that have identity and supertypes. You might not use those features but they can't be ignored without compatibility concerns.
if you were to add an artificial companion in each module that wants to add static methods to a type that lacks it's own companion inject, identity/equality breaks
if you don't add an artificial companion, then what is the receiver of the extension function?
if namespaces are introduced as a new concept without the same constraints as companions, then it can also work differently
c
So essentially we'd be able to add extension functions on ‘a class' namespace', which would work pretty much the same way as if it was on a companion object, except it doesn't need a companion object to exist, and stuff like
this
doesn't exist in it?
n
personally I'd vote for 1; just better optimize things, at least for the time being. I don't really understand why it's so compelling to specifically use the Class.methodName syntax
seems like that is better solved by giving a bit more flexibility to
import
let the user choose based on how polluted their global namespace is, or isn't
Most languages have this kind of flexibility in their import system, IME
python, Rust, C#
I don't think it substantially helps with either, probably not enough to bear the weight of a new language feature, although it's not a very complicated language feature, so it's a pretty meh decision either way.
e
I'm not sure what you mean there. I assumed you meant something like C++'s ADL, but that's a significant departure from how name lookup works now
n
@ephemient was that addressed to me or Ruckus
python doesn't have any form of namespaces in the way you're thinking of adding to Kotlin
I don't know Rust as well; so maybe you could point me to what you mean. In Rust, a namespace is anything that binds names inside that scope, but there is nothing that introduces a scope with no other purpose that I can see, outside of modules: https://doc.rust-lang.org/stable/reference/names/namespaces.html
In other words I don't see anything in Rust that would parallel this suggested feature in Kotlin
namespace in C# still seems more like
package
in Kotlin, overall. C# namespaces aren't associated with a class, the way that namespaces in Kotlin are proposed to be 🤷
e
Kotlin already has package namespaces, although they aren't first-class - e.g. you can't
import java.util; util.Map
, but you can
import java.util.Map; Map.Entry
n
yes, but you can't afaik do
import java.util
and then
util.Map
e
that's what I said?
n
ah, ok
e
for historical reasons, I don't think that can change, as somebody could be evil and write a
package java; val util
and then it's ambiguous what
java.util
refers to as a term
with
namespace
as a new concept, it could define what a naming conflict means (probably forbid it)
n
compiler error if it's ambiguous? doesn't seem like that big of a problem
e
compiler error on usage? that would prevent you from using existing code that works now (even if it is badly designed)
namespace could give a compiler error on declaration, which is better
n
yeah, the question is how much real code actually breaks this way
Yeah, I don't claim to fully understand all the nuance here or anything either so don't worry about it 🙂
e
it's light on details but I'd imagine that if it were followed up on, it would be implemented similar to top-level functions now, which (on JVM) are translated into a class hidden from Kotlin and exposed via metadata
n
I'm not super against such things, but the benefit seems meh, and on the flip side, Kotlin is very unusual in not giving you the option to use qualified names in your actual code, basically, I'd like to have that option regardless, and once you give that option it further diminishes the need for namespaces
Yeah, I could probably re-read more carefully as well. I skimmed the post, and also skimmed the talk a bit ago where the idea of namespaces was also mentioned
Well, yes, partially qualified names, but also preventing the global namespace pollution
you can do
java.util.Map
but
Map
is still in the global namespace
in python if you do
import foo
then you still need to refer to any entities in foo qualified, i.e.
foo.bar
will work but
bar
will not
e
as an aside, Scala, Rust, Python all have a similar
Copy code
import java.util.{List, Map}
use std::collections::{Vec, HashMap};
from collections import UserList, UserDict
which is a nice QoL improvement
n
if you do
from foo import bar, baz, qux
then now you can use the unqualified names. So, you pick, qualified, or unqualified, or partially qualified if you want, and only the name you want gets brought in
Rust affords you a similar level of control afaik
Kotlin is the only one where everything you import always gets dumped into the global namespace
e
going even further, Haskell lets imports rename or merge package names, qualified and unqualified, while also providing inclusive or exclusive filters