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
r
@CLOVIS Introducing
static
into the language was only one of the three proposed solutions for that issue. Can you also explain how your example compares to
namespaces
? It seems that would solve exactly your problem in a more elegant and robust way.
c
My understanding of namespaces is that it's just a compiler thing that makes a ‘simile-companion' on-the-fly?
r
I'm not sure what you mean by "simile-companion", but I find it easier to reason about the other way around. If namespaces are a thing, than a companion object is just a convenient way of handling state in the class's namespace. (I have no idea how "correct" that reasoning is. Also, it's worth noting that namespaces can also be declared independently of class definitions.)
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?
r
That's pretty much the sales pitch for namespaces in the blog post:
[...] a kind of ephemeral object without an instance [...] keeps static helpers grouped together in the source, but removes all the object overhead.
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
r
Namespace pollution is already a problem. Say you have a function like
fun thingFromMap(map: Map<String, *>): Thing
(where
Thing
is from an external library). You could change it to
fun Thing.namespace.fromMap(map: Map<String, *>: Thing
, and you wouldn't need to worry about the function polluting the global namespace. (I'm sure that wouldn't be the syntax, but I don't know what it would be, so...)
n
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
r
I don't entirely agree. Having functions associated to a namespace can help a lot with discoverability and readability, not just code completion. Better imports is unrelated, and both could be done.
n
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
r
Indeed, but that's a separate issue. As evidence by all three of those having some form of namespaces.
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
r
Ah, indeed. It's been a long time since I did python. I must have been remembering something else. I think the point still stands with regard to Rust and C# though.
n
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
r
Namespaces wouldn't be associated with classes in kotlin. They would be a separate thing, and all classes would just have an implicit namespace.
n
yeah, the question is how much real code actually breaks this way
r
Actually, I guess that wasn't in the blog post. The blog post only mentions classes. I may be a ways off here...
n
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
r
Indeed. I think I may have made some assumptions about the proposals based on previous discussions about the concepts. I should probably go through and read the blog post more carefully and see what is actually in these specific proposals.
n
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
r
Kotlin is very unusual in not giving you the option to use qualified names in your actual code
Could you elaborate on that? As far as I'm aware, you can use fully qualified names in Kotlin. Or was this more about partially qualified names like in the
util.Map
example above?
n
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