It’s interesting, I still haven’t seen any example where statics would actually be superior to companion objects (or package-level elements, depending on the specific circumstances) from you, except for the familiarity argument, which is not a valid argument in my opinion.
They are not. They are trying to fix badly designed language features. Which implicit nullability, statics and the likes are very much. There are better alternatives that give the programmer just as much freedom with much better possibility to reason about the behavior of code. After working in Kotlin for an extended time I’m aggravated everytime I come back to Java code and have to start guessing again where NPEs could potentially happen. No thanks, I’m very much keeping the seatbelts even if it means I have to take an additional second to unbuckle before getting out of the car. I have never seen a case where having platform types would actually be beneficial. Everytime I thought I had one it was actually poor design of the program logic that needed restructuring, and didn’t require platform types in the end.
The only thing I agree about are exceptions - I’m split on that, not sure if I like the “unchecked-only” approach, but I see why they did it.
Default values for class properties belong in the companion when they are really for that class, not at global level. If they are shared among multiple classes it is really important to put them into an object as an object allows making it configurable (or even a class) in the future (Frankly the object should be an implementation of a generic interface where the object provides default defaults).
If you have a “static” helper method private to your class you can put it either in a companion object or as a private toplevel in the file that declares the class. In that case you should think careful when you encounter name collisions on whether the functions are not actually in need of refactoring and combining.
If the difficulty of students to grasp the meaning of static in Java is anything to go by, it is indeed a bad feature. The concept of everything is an object leading to java.lang.Math certainly is bad design (it is a class functioning like a namespace where packages are already doing that).
The problem with checked exceptions is that the choice is made at the declaration site rather than at the use site. It breaks any context where you effectively have callbacks that may do things that throw exceptions. Very common when doing database/parsing etc in combination with other IO. The callback is configurable but ends up catching exceptions, wrapping them in a RuntimeException and then rethrowing that, or alternatively having many declared exceptions or overly broad ones to handle this. Sometimes unwrapping it at the place where the callback was provided (outside the utility functionality that the callback is provided to).
The result of checked exceptions is generally overly broad exception signatures, and unwanted clutter due to catching and rethrowing exceptions in code that doesn’t really care. The only viable solution would be usage dependent exception signatures (the signature becomes context dependent/generic) in combination with some sort of language construct where you can indicate that all non-local exceptions should be caught in that function. (non-local to allow for throwing and rethrowing within the body). Until such a feature is available unchecked exceptions lead to overall better code than checked exceptions (which also very easily leads to implementation details through exception signatures). Of course a functional approach using some sort of maybe type can be even better ;-).
I can’t say about everything, but as far as Math goes, it is definitely bad solution. The problem is that there are different ways to do math. For example there is Commons Math FastMath replacement. Also, one seldom wants to apply mathematical operations to non-primitive objects. The ability to define the interface and then have multiple object implementations of it looks much better.
Kotlin context-oriented abilities allow to go even further and to use this object as a context for computations using with or similar constructs. You can’t do it with statics.
It seems I inadvertently triggered a quite lively discussion. There seem to be two camps: the “it does not fit/it’s not oop/it’s not needed camp” and the camp I’m in which does not agree with that since IMHO it fits perfectly and is minimally disruptive, it is needed because it currently is creation friction when doing mixed java kotlin projects.
The OOP argument in particular I don’t buy and counter with the notion that static methods are perfect for functional programming since they are stateless. Kotlin has borrowed plenty from the functional programming paradigm and is neither a pure OOP nor pure Functional language, just like Scala and Java.
Anyway, I’m not going to repeat the arguments above. I get and respect that people have different opinions and that the dominant opinion seems to be we’re not doing this because Kotlin is opinionated on this topic. I respectfully continue to disagree with this and haven’t read anything to change my mind.
Enough said on this topic. Next time this comes up, and I guarantee this will come up a lot; these things have tendency to drag on, maybe consider if simply adding static would have been a better use of your time than trying to educate people just about how wrong & misguided they are. IMHO if something needs a lot of education and arguing, you need to step back and maybe reconsider if you can do things in a more intuitive way.
I hope people can agree that too much energy is being wasted on this particular thread, which sort of proves my main point that this issue is real and that kotlin language designers are and have been on the defensive on this topic. Anyway, moving on.
That is such an unsatisfying argumentation. If the need for education and arguing would be a reason to just keep the old way, because it’s easier in the moment, science would have gotten nowhere. Society would have gotten nowhere. There would hardly be any advancement at all.
You say statics are more intuitive, but why is that so? Simply because lots of people coming from Java are used to the concept, and companions / package-level are new concepts for Java devs. For newbie, I would dare say, the latter ones are more intuitive.
The goal of Kotlin is not doing everything the way Java did, that would be pointless. It is doing a lot of stuff Java does better, while still being reasonably easy to learn for Java devs. If you’re expecting to not have to learn at least a few new concepts to take advantage of the language, you’re obviously in for disappointment.
I feel like it would help most people to watch yole presentations on writing idiomatic kotlin. - YouTube Idiomatic Kotlin
Also seeing as Andrey Breslav has mention a few times even at KotlinConf that they’ve been thinking of removing companion objects this topic should be simplified at some point.
I got used to companion objects but still prefer static.
I’m dealing with memory & performance sensitive code so I sometimes need to resort to tricks such as @JvmField annotation in order to get what I want (even if I don’t need the Java interop). The only alternative for me is top-level declarations which pollute the namespace.
an expression whose meaning is not predictable from the usual meanings of its constituent elements, as kick the bucket or hang one’s head, or from the general grammatical rules of a language, as the table round for the round table, and that is not a constituent of a larger expression of like characteristics.
Idioms in any programming language help you get your bearings and provide structure when you start writing code. But, as John O’Hanley points out, some Java programming idioms are both widespread and harmful, having a negative influence on code maintainability.
Your citation does not represent very well what “idiom” means in the context of the linked talk, or in the context of programming in general. In programming doing something in an “idiomatic” way usually just means “there are multiple ways to do it, but this way is most likely to be immediately understood by the people looking at your code”. Actually, Kotlin needs much fewer idioms than Java because it offers built-in ways to do many things that in Java you have to build yourself, which leads to lots of patterns you need to learn to recognize what you’re looking at, as opposed to just looking up what that language keyword or syntax means.
Also, this is in no way relevant to statics vs. companion objects. Both are concepts completely unknown to someone new to programming, and, on the risk of repeating myself, I’d argue that companion objects are easier to grasp for that someone.
My comment is relevant to this discussion. The keyword “static” and the concept it represents are well known in the context of many successful programming languages. And the noun phrase “companion object” is a great example of an idiom.
The second sentence quoted above is nonsense. All concepts - “assignment”, “if”, “goto”, “null”, “for”, “switch” - are unknown to someone new to programming.
“Successful” doesn’t equal “all features of it are well designed”. Just because a feature is widely used doesn’t mean there are no better alternatives. Carrying over all the things from the past is what Java is doing, and what Kotlin wants to avoid.
No, by definition, it is not. It’s defined by the language, it’s part of Kotlins grammar, if you will. Look at your own quote about what an idiom is, it’s supposed to not be predictable even if you know the grammar of the language.
This whole discussion boils down to “People coming from Java might be confused by companion objects, but are probably familiar with statics. Is that an issue big enough to warrant sticking with the inferior solution?”, to which I myself would answer “no”. Software developers are usually smart enough to figure out one or two new concepts. It’s not like Kotlin is going full Scala here.
I agree–we can’t have a discussion keep redefining each other’s words. When speaking about Kotlin, I use “idiomatic” to mean, “clear and understood naturally”. In case it wasn’t clear, we all want easy-to-understand code.
I think this thread has strayed away from productive, value-based arguments and into unproductive position-based arguments.
Some positions:
Static is better than companion objects. Remove companion objects and replace with static.
Companion objects are better than statics. Do not change anything.
Our values:
Clear, clean, easy-to-understand code (idiomatic Kotlin–using “idiomatic” in the same meaning as it is commonly used in Kotlin)
Avoiding language designs that limit our ability to write better code.
Including language designs that enable us to write better code.
I suggest we abandon both positions as we share these values in regard to this discussion. The person disagreeing with you also wants beautiful Kotlin code even if they love/hate companion objects.
What are the exact issues people face with companion objects?
Is it the syntax of having to declare things inside the companion object? Would other language changes, such as syntax similar to the following resolve most of the issue for you?
companion fun someCompanionFunction()
Is it a performance issue with companion objects? If the performance was on par with statics, would the issue be mostly resolved?
Is it a scope/namespacing issue? Would other language changes such as something like the following resolve most of the pain points of companion objects?
import top.level.functions.* as Utils // Utils.myTopLevelFunction()
Is it an issue with interoperability (using Kotlin from with Java/JS/Native)?
Is it an issue with writing interoperable code from within Kotlin?
Is the main pain point from previous experience (experience with Java statics)?
I’ve listed all of the root pain points I could think of off the top of my head. Please add any I’ve missed.
Which of these points (or others) would need to be resolved before it would become a non-issue for you?
Rant about this thread
The above are discussion points I would really enjoy discussing and hearing others opinions on. As for statics, Kotlin has companion objects. Let’s identify what pain points exist and if they could be improved (if there are any).
Petitioning for statics is frustrating in that it blocks productive discussion on what the real pain points could be with companion objects.
One of the most important aspects of the success of any programming language - from Fortran to C to Java - is upward compatibility of source code. The designers have Swift have really done damage to the adoption of their language outside of the Apple ecosystem by continuing to tinker with the syntax and semantics of the language, therefore making source code incompatible from release to release.
For Kotlin to not repeat that mistake, companion objects must stay the way they are, or be modified in a manner that is upward compatible. Quite frankly, I can’t imagine any changes that wouldn’t just dig a deeper hole, so my recommendation is leave it alone.
The real problem I have with this discussion is that folks like arocnies dismiss that addition to the static keyword to Kotlin out of hand with an air of absolutism that is off-putting, to say the least.
Further complicating the discussion is this: there does not seem to be any consensus here as to whether companion object is or is not a usable replacement for static.
I would not accept that claim without support. I acknowledge that it definitely was true of the migration of C to C++, but not aware of other cases where that is generally true.
It is true for Kotlin at the binary level but not the source code level