I'm having a particularly hard time convincing our...
# codingconventions
k
I'm having a particularly hard time convincing our lead architect to let us adopt idiomatic Kotlin usage so I thought I would post here to see if anyone has any recommendations regarding some specific points. So in general it appears he's fine with Kotlin as long as we follow the same style guide as his C# team does. His justifications really lean into the philosophy of eliminating all subjectivity regarding what the code looks like and he wants consistency throughout the disparate codebases rather than consistency with the specific ecosystems. Here are the two of the biggest points he's made: 1. One class per file. His justification here appears to be that if we allow multiple classes per file then how do we decide what can go together and what can't. The official style guide's position of "closely related to each other semantically and the file size remains reasonable" is too subjective for him. If we follow this reasoning of "objectivity" then I find it hard to come up with an argument in support of multiple classes per file but this just feels wrong. For one, I now have dozens of basically 1-line data class files. More importantly, if we can't have multiple classes per file we can't use the sealed class language feature. 2. No inner or nested classes. It seems he wants all classes to be top-level with explicit names. This once again leads to more files but also longer file names. It also goes against precedence set by platform classes like Map.Entry and in my opinion just pollutes the name space. We also can't use private, implementation-detail classes. Any suggestions regarding how to justify these points "objectively" are appreciated. Note that "this is a best-practice" and "this is idiomatic" do not appear to be sufficient.
Thread for discussion.
b
One good reason for closely related classes in one file is as you pointed out sealed classes. A good argument for those is exhaustiveness checks in
when
expressions. This is one of the most powerful systems in kotlin and limiting this is a bad idea. I don’t know about inner/nested classes. They are great for stuff like
Map.Entry
and I don’t see any good argument why you should forbid them. I would argue that they lead to a cleaner namespace. Othrewise one class per file is ok. Ok, maybe it’s overkill for 1 liner data classes but in the end it does not hurt. I would just make an exception for nested/sealed and private classes. Private classes belong where they are used and sealed classes are just too good to ignore.
Also out of interest, in C# methods are normally capitalized. Does he also want you to do that in kotlin? Because that would IMO look terrible in combination with any other kotlin/java library out there.
🧌 2
k
Thanks for that. I don't have problems with one class per file for classes that have a decent amount of behavior/code. In this particular case though we're talking about a Models.kt file filled with data classes that correspond to a specific part of the json data model returned by a REST API.
Ha. He did not mention that (yet).
He didn't seem impressed by sealed classes. I wonder how much of that is due to
sealed
existing in C# simply as a
final
class.
k
Another point against his "can't decide which classes to put together" is that now this just moves the problem up, ie. which files do you put together in the same package? Also on of the major advantages of Kotlin is how easy it is to just declare small throwaway classes so you don't have to resort to Pair with badly named properties or similar bandaid solutions.
2
t
tbh, I am not getting too much the more public classes per file things. It just looks like the single file now acts as a package. I see the convenience for small classes, to have all of them in one screen rather than having to jump from file to file, but from a design point of view a
kt
file conflates the definition of package imo. I use multiple classes per file however, especially for class hierarchy and default interface implementation (and sealed classes as mentioned) regarding your second point, that does not make any sense, with inner/nested classes you can use private classes to make your code more readable and prevent usage outside of where the class is needed
k
Karel, thanks for that. I was thinking the same thing but couldn't verbalize it for some reason.
Marco, I don't know that I would agree on the package argument mostly since the concept of a
package
already exists and multiple classes per file don't change what package those classes exist in. I'm not arguing that everything should be in one file but when you have a bunch of 1-line data classes that are very related (data binding a Rest API) and the file is well named, I don't see why those should pollute the screen space/file system by existing separately.
h
inner classes are effectively the only way i know of to mimic the behavior of pointers. take this example:
Copy code
class Vector2(var a: Int = 0, var b: Int = 0) {
    fun reversed() = Vector2(b, a)
}
every time you call
Vector2::reversed
you are instantiating another object. You can't set the value at construction and forget about it because then you are not accounting for changes to
a
and
b
objectively the more space/time efficient way to accomplish this is to use inner classes: https://pl.kotl.in/L-s2-E7Fe now, i understand this is not the best single example, because it seems a bit convoluted, but this approach can be extremely effective.
m
I think the "one class per file" mandate is silly, but FWIW your Vector2 example can easily be achieved while still honoring "one class per file", if you simply externalize Reversed and have it accept the outer Vector2 as a constructor parameter.
h
That's a good point. Structurally it does make a lot more sense to be an inner class, however. plus it feels less sketchy for an inner class to alter the values of the outer than for an entirely different class in another file to do that.
👍 1
b
With inline classes there might be an advantage to not use an inner class. That way you don’t have an additional heap allocation. But even then I would argue that the
Vector2Reversed
class should still be in the same file, maybe even nested. I’m not sure if inline classes can be nested.
h
I'm not familiar with inline classes. Could you please provide me an example of how to accomplish
Vector2
using them?
b
If your not familiar with inline classes you should take a look at this: https://github.com/Kotlin/KEEP/blob/master/proposals/inline-classes.md The short version is that inline classes are an efficient way to write classes that have only 1 field. In that case the compiler can inline the type which prevents extra allocations. It’s great for wrappers especially of primitive types. They are still experimental though. I don’t suggest using inline classes for
Vector2
. My idea was to keep
Vector2
the way it is, but instead of implementing
Reversed
as an inner class you would use an inline class.
Copy code
class Vector2Impl(override var a: Float, override var b: Float): Vector2
inline class Vector2Reversed(private val v: Vector2): Vector2 {
    val a get() = v.b
    val b get() = v.a
}
Well I guess there is one problem with them. They can’t extend classes only interfaces, so you would need to promote
Vector2
to an interface. Not saying this is the best solution, but it is another option.
g
In terms of quality time spent, I'd prioritize updating CV over trying to bend a "C# architect" that for some strange reason has authority to tell kotlin team where to put classes.
k
Ha. That was honestly my position about 90 minutes into the first meeting.
😀 1
t
Not helpful, but this discussion reminded me of the Python developer trying out Java ...
🙈 1
My approach to your such a "lead architect" would be: Ask for forgiveness and not permission. Would he notice, if the Kotlin team puts their data classes in the same file? Does he review your pull requests? If so, can you ignore/outvote him?
👍 1
You could also address your underlying issue directly (to the architect first and then maybe escalate?): That you feel that the restrictions by your lead architect are ill judged and intrusive. When doing so I would strongly suggest to send I-messages. But this goes into communication psychology and is not so easily explained here. Do you have a scrum master or Agile coach at hand?
2
d
How would your Architect feel if he reported to a manager who was really comfortable with Haskell; and insisted that the architect restrict his C# usages to follow some Haskell idioms? Pretty annoyed I would think. This is just so wrong. Languages should follow the idioms of that language, not another. What can be common is development processes and standards; not low-level formatting.
1