-
Notifications
You must be signed in to change notification settings - Fork 362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Kotlin statics and static extensions #348
Comments
Great write-up, but there doesn't seem to be any information about the ABI of platforms other than the JVM. What's the ABI of JS, and is it blocked/require ES2015 output from the compiler? Does the Objective-C interop use class methods or something else? Also curious if external static function declarations work on JVM and JS? |
(on behalf of @altavir) Also it would be nice to consider using The question of interoperability with JS is also interesting in the context of https://youtrack.jetbrains.com/issue/KT-46164 |
The concept of "static sections" could be generalized to "modifier sections" with equivalent private and internal sections. Each modifier section would then apply its modifier implicitly to all contained members. In cases where it would favor readability, these sections could be collapsed to regular modifiers as used presently. While the proposal explicitly rejects such a hybrid approach for static, the more general case may be worth considering nonetheless. |
Great proposal. Only one observation for the JVM mangling scheme - Other alternatives:
|
Thanks for the proposal, this looks very promising. Personally, I lean towards static sections, the only thing I don't appreciate is the verbosity of a single static constant property declaration. I understand that supporting both static sections and static modifiers generally is not an option, but what if this would be dependent on the declaration type? E.g., static sections could be required for functions, extensions and getters, whereas only static constant properties could support the static modifier syntax (exclusively, or in addition to static sections). This would solve the verbosity issue and only slightly increase complexity. The code style discussion remains, but for constants only it isn't as impactful, from my perspective. |
Great proposal! Few concerns that popup in my head. Will I'm not a fan of |
Another suggestion - the distinction between |
Alternative proposals:
Pros of 1):
Cons of 1):
But those cons are solved in option 2):
|
I agree with the suggestion to replace
The exact same rationale is also applicable to |
I also like |
I even would go further and suggest allowing |
I see there's suggestion for further improvements to use Does it mean that regular |
|
Can someone explain me please the following: in the example class Outer(val one: String, val two: String) {
static {
fun createMappings(): List<String> =
setOf(::one, ::two).map { it.name } // WORKS! No need to write Outer::one, Outer::two
}
} because val l: List<String> = Outer::createMappings() My problem with this is that The example looks wrong to me, but I assume it's not wrong and I'm just misunderstanding something. |
Overall I love this proposal but I echo the sentiments of using Also seems to be an unpopular opinion but I favour the modifier syntax over the sections. This is just my personal opinion but I feel the verbosity of a whole separate section makes this feel like a shorthand for just declaring a companion object. On that note, I understand the syntax for static extensions makes a ton of sense when you use sections, but from the modifiers perspective would something like |
This would conflict with Extension functions as static members but I agree with you and don't really like |
@TheBestPessimist |
Since "Extension functions as static members" is allowed, I think we absolutely should allow static operators that can be imported as you'd expect. That would allow more controlled scoping of operators. Maybe also instead of the |
I wanted this in Scala 3 to easily add extension methods for both scala's |
@Mr-Pine |
Notes:
Shouldn't |
@spen37 It looks like |
I like Having a static extension as a static member will probably always be a little confusing because you have to signal, that it is "double static", but I like @kyay10's of declaring static extensions as |
There is a |
Great proposal. interface Parseable<T> {
fun parse(s: String): T
}
class Color(val rgb: Int) : static Parseable<Color> {
static {
/*override*/ fun parse(s: String): Color { /* impl */ }
}
} |
I like that style of static implementation, but If the interface does not know if it will be implemented statically or not, what would happen if illegal interface Parseable<T> {
fun parse(s: String): T
fun something() {
println("I am $this") // no instance of `this` statically
}
} |
For static inheritance, it would also be nice to be able to mix static and non-static abstract methods, e.g. interface Serializable<T> {
abstract static fun deserialize(s: String): T
fun serialize(): String
} You'd need some way to specify that the static method in the interface is abstract, which is what I used |
It's indeed confusing to have I agree |
As I like the grouping aspect that automatically came with companions, I highly favor the grouping syntax here. 😊 |
@Peanuuutz Actually, I think this definition depends on whether the design will adopt the "companion namespace" idea, where conceptually inner namespaces (i.e. inside a class) and external namespaces are treated as an entity, just like object/companion object, or if the current proposal will be kept. If going with the companion namespaces idea, I agree with you, though replacing in your example the inner namespace with |
True, but we are used to |
In Java it's a block of code, it cannot contain member declarations |
Static section is useful for distinction between static members and non-static ones, and just a few keyword changes from companion object. Yes it writes a little bit more, but I doubt that is a problem actually. Unlike companion object, where you could add extra annotations on it, static section is just a bare block, which means for IDE it's fine to automatically add parentheses and move into it. The only real mess I'd point out is it differs from Java, where static block is for initialization of static members. This is another place I found more reasonable to use |
So, you've picked the name "static" because it is the one used in other programming languages, yet you consider it OK to have a From my perspective, there are three good options:
I personally would like to see both (1) and (3). That is, the addition of This gives people the choice between statics and companion objects. The companion object syntax doesn't change except for the new performance-optimizing keyword. So, existing Kotlin developers are happy, and Java developers coming to Kotlin are also happy. Among the feature barriers preventing my Java team to switch to Kotlin, statics are probably the biggest offender - some Java frameworks (especially logging) are a lot more complicated to use in Kotlin due to the lack of statics. For example, currently to introduce a logger in the Java style, you have to declare it in a companion object. In a smaller class this involves adding 3 lines, while in a bigger class this involves finding where the companion object is already in the code and declaring the logger there, but this is hard to find compared to the Java style of always declaring the logger at the top of the class. Java:
Kotlin:
Even "idiomatic" Kotlin logging frameworks are not ideal as they require you to declare the logger outside the class definition - this means above the class's KDoc documentation and thus in the same lump of code as the import statements. Not where you expect a logger definition to be. Plus, you can't use different loggers between multiple classes in the same file.
Adding the
Another use case for static modifier is when you need a helper constant for just a single method and you want to put it together with the method. It doesn't make sense to put it in the companion object which might be far away from the method definition.
Another example use case with
So in conclusion, I think static modifiers absolutely have some irreplaceable use cases, and are consistent with other languages, so they should be added to Kotlin. Then, to cover the use case of a "more efficient companion object", I think the keyword If the static sections are added instead, then they should be called |
I would too like to see a similar unifocation, that everything inside the brackets is a part of the definition. E.g. variables are a part of a frame which the function defines and properties are a part of an object which the class defines. If it's just something statically hosted in the parent's namespace, define it as such with 'namespace' (or similar) keyword/scope. It could also allow to do it inside functions: fun verify(value: String): Boolean {
//...
namespace {
val expr = Regex("...") // cached instance of compiled regex
}
return expr.matches(value)
} |
@Peanuuutz |
Yeah I know that. It's impossible for Kotlin to change how nested/inner class works nowadays. 😄 |
I'm against this. This is adding global state implicitly, which, as we know from C's |
Did you mean global state in general or global mutable state? In the example given by @mcpiroman, |
Even immutable values should be declared outside a function, because function itself shouldn't contain any persistent state (aka, not functional). It is the responsibility of a class/object to hold those (for top level properties, the holder is the package). |
Do you mean you always write private const val acceptedExtension = ".txt"
fun isTextFile(name: String): Boolean {
// check exists...
return name.endsWith(acceptedExtension)
} instead of fun isTextFile(name: String): Boolean {
// check exists...
return name.endsWith(".txt")
} ?
You are right that mutable global state declared inside functions would be a problem. Well, mutable global state declared anywhere is a problem. I mostly meant this feature for rather small, temporary constant data which does require allocation or initialization, such as But this is quite a different feature than static extensions. I have some more points to back it up, but maybe its better do discuss it elsewhere? |
To prevent that this kind of global state can be added in functions, statics should be forbidden in local classes (local singleton object declarations and companion objects for local classes already seem to be forbidden). |
Let me summarize my impression from this discussion. I like the idea of
|
The problem here is not whether you store something, but where you store them. Functions are not supposed to be this where. That's what I want to say. Even if we went this path, how could you avoid bad practice like having a persistent mutable list? I'm pretty sure it would be a ktlint rule immediately. If you only allow |
I would keep in mind what namespaces are to the average user -- inline objects. We're basically inlining objects that don't need a type. Keeping this in mind, in my honest opinion, inline object Kointer
// vs
namespace Kointer
class Kointer {
inline companion object
// vs
companion namespace
} Additionally, I would support restricting namespaces to only support And lastly, if we choose the // <missing package>
namespace MyLibrary { } // error, namespaces must not be used as packages
package org.example.mylibrary
namespace Utilities // error, useless empty namespace, does nothing
namespace Utils { } // error, useless empty namespace
namespace Util { // ok
fun meow() = TODO()
}
(also, what a small world, the library I'm using just mentioned this issue while I'm commenting on it) |
To me namespace is much more clear than "inline object". For most of the time, when I'm thinking to have an "object", especially companion object, I actually mean to have a namespace, or companion namespace, so that something is shared (not instance based) in a more encapsulated (not top level) way. Namespaces must be able to declare non- Note that you could have extensions on namespaces. Empty namespaces may act like a semantic scope. |
The most interesting part for me is still the concept of a static interface Creatable<S : Creatable<S>> {
fun create(): S
}
fun <S, reified T : Creatable<S>> foo(): S = T.create() (see KT-49392), it could also be used to cover other features from the proposal. First of all, it could be used as a namespace, replacing static interface Delegates {
fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = ...
}
val property: String by Delegates.notNull() Related: KT-16900 Next, static interface MyStatics {
fun foo()
}
class MyClass : MyStatics {
override fun foo() { ... } // this is static
fun bar() { ... } // this is not static
} This would only work for public members of the class. For private members, another mechanism could apply: Any private member of a class is compiled to a static member if and only if it has no reference to |
@thumannw I think it is better to add static modifier for such interface members instead. This way you would be able to have both static and non-static members in the same interface. Also, you might like self types. |
At first I thought Here is why:
My proposalstatic object Foobar {} // ERROR, not supported
// other.kt
namespace foo {
val someValue = 0
}
namespace foo {
namespace bar {} // nested namespace
val answer = 42 // namespace level decl.
init {} // ERROR: not allowed
class Color(val raw: Int) {
companion {
init {} // OK
fun fromString(): Color // factory fun
}
companion val Black = 0xff000000 // some 'static' decl.
companion object {
...
}
}
} PS: another syntax I have considered is using class Color {
Self {
init {}
}
val Self::Black = 0xff000000
// companion object becomes:
object Self {}
} Static extension syntax
|
@zhelenskiy |
May I ask that when this comes out, will there be code style migration or preference towards |
Hi guys, what about something like static expressions? |
@revonateB0T Your proposal is unrelated to the topic. Please refer to https://youtrack.jetbrains.com/issue/KT-14652 |
Static expr is not always computed in compile-time. |
@revonateB0T you can easily create complie-time memoization on a library level. And is still have nothing to do with the discussed topic. By the way, this confusion is additional argument to avoid word |
This is an issue for discussion of Kotlin statics and static extensions proposal. This proposal is the culmination of our research and design on KT-11968 Research and prototype namespace-based solution for statics and static extensions.
The full text of the proposal is here.
Please, use this issue for the discussion on the substance of the proposal. For minor corrections to the text, please open comment directly in the PR #347.
OPEN ISSUE: This proposal has a major open issue on what kind of declaration syntax to use for static members: static sections or static modifiers. See Static section vs static modifier for details on their pros and cons. The text of the proposal is written showing both alternatives side by side.
Let's use this issue for an ad-hoc voting on the syntax via reactions:
Please, read the proposal before voting!. If you are just excited about the future introduction of statics in Kotiln, then react with 👍. If you don't like the idea at all, then react with 👎 and explain your concerns in the comments.
The text was updated successfully, but these errors were encountered: