Point Operator to access field

Hi
I know the get/set operator override to access object’s data fields. But Im missing (regardless of all drawbacks) a point operator override to access fields. Also to allow adding “new” identifiers at runtime.
It is possible in PHP and Javascript, e.g.

var obj = {
    firstname: "Donald"
};
obj.lastname = "Duck";
console.log( obj.firstname );
console.log( obj.lastname  );

This could be quite useful and also improves readability a lot.
(Im aware that PHP / JS are interpreters that allow weird stuff…)

If you are targeting JVM, there is no way, as the JVM does not support that.

If you are targeting Javascript, you can do that in this way:

var obj = js("""{
    firstname: "Donald"
}""")
obj.lastname = "Duck";
console.log( obj.firstname );
console.log( obj.lastname  );

Alternatively, with Javascript target, this will work also:

val obj: dynamic = Any()
obj.firstname = "Donald"
obj.lastname = "Duck";
console.log( obj.firstname );
console.log( obj.lastname  );

Yeah, I know this js(“{}”) shortcut.
I was just wondering if this very useful feature could be part of the language standard, somehow…
Thought Kotlin is a universal language not only bound to the JAVA VM.

I don’t think this feature is good for kotlin. It would just break all securities the type system offers. What you describe is basically a dynamic type. Kotlin is designed to have a strong type system so this goes against the design.
Also, you could just use a Map do do exactly that. Ok it would require some casting as the map would probably need to be of type Map<String, Any> but if you want to use a dynamically typed language, there are many out there to choose from.

3 Likes

It is not about JVM, it is about being static language. Kotlin is static and JS features are there only for seamless interop. In kotlin you can emulate dynamic structures by using String-keyd maps. But in gereal it is just a different language paradigm which requires different approach to program design. By the way, adding fields dynamically to an object is generally considered to be a bad practice even in JS and Python.

Also if you really need those features, you should use Groovy. It could exist in the same project with Kotlin.

4 Likes

Yes, Kotlin is a universal language. But more importantly, it is a statically typed language. What you wish for will “never” be added to Kotlin language in a universal way. In Kotlin, there are other ways to achieve goals with similarly concise code.

3 Likes

Maybe its obvious … but this can be done in kotlin at the class level instead of the object instance level.
i.e.

class cobj  {
    val firstname : String =  "Donald"
};
val cobj.lastname  : String get() = "Duck"
val obj = cobj() 
println(  obj.lastname )

This is not a solution to the problem unless you want everyone to have the lastname “Duck”.

I didn’t get your point. The main reason for using [Name, Value] paired hashmaps is to dynamically add (or remove) tuples during runtime, that’s what they are made for. Why should this be bad practice?

How “static” or strict are constructs using Any? as a type?
This will break everything!
If you’re really writing concise code, you’ll have to omit that Any-stuff completely.
Ok, sometimes, or better, most of the times, you should really stay with strict typing.
But there are exceptions where you do need to break out some rules. Why should a language support Any? otherwise if there’s no need for it?

I write a lot of Kotlin-JS for browsers and the browser API is pestiferous with type-less stuff, you know that. And that type free JS VM became the most successful software in the world. Were the creators all wrong…?

All I wish, is a little syntactic sugar that allows me to phrase my code less verbose. But, ok, Kotlin offers tons of paths to the goal.

I just hate the name indexed writing style with the square brackets.

You may want to have a look at a more Kotlin like approach to cope with that!

1 Like

JS was successful as the first standard programming language for browsers. Now, the success is for static typed language, as kotlin, typescript, php (that was first chosen by facebook team who then create Hack and finally obtain a static type version of PHP (because large codebase became a nightmare to maintain).
Usage of Any should be an exception. In you’re case of a Container (kind of properties), union types should be a better solution.

I’ve mentioned earlier. It is a great language (Kotlin got a lot from it), but it did not manage to get significant popularity exactly because of its dynamic nature (even with optional static typing).

1 Like

I actually like your approach, of building your own small DSL.

Coming from Ruby, where using dot notation for maps is considered a good practice, I can say that it becomes a total mess pretty quickly.

You can also make use of sealed classes to make your code a bit safer.
Example

The JVM allows it. Groovy has the Expando class. You can create your own custom class that extends Expando, then you can add fields and properties to existing objects. You can also dynamically modify the class itself so that all future objects have the new fields and properties. So, given this:

class ExpandoClass extends Expando
{
	def firstName


	public ExpandoClass(Object firstName)
		{
		this.firstName = firstName
		}
}

you can then do this:

	ExpandoClass expandoClass = new ExpandoClass("John")
	println expandoClass.getFirstName()
	expandoClass.lastName = "Smith"
	expandoClass.getLastName = {return lastName}
	println expandoClass.getFirstName() + " " + expandoClass.getLastName()
	println expandoClass.firstName + " " + expandoClass.lastName

Disclaimer: I’ve never learned or used Groovy.

Having a quick look at the Javadoc of the Expando class I can deduce that what happens in something similar to what Kotlin does with extension functions, that is that the compiler maps each extension function to a static function with an additional argument being the receiver.

So what (I think) the Groovy compiler does is that it maps each property access of a class extending Expando to a getProperty()/setProperty() call, for instance:
expandoClass.lastName = "Smith" gets translated to expandoClass.setProperty("lastName", "Smith")
and
println expandoClass.lastName gets translated to println expandoClass.getProperty("firstName")

This means that the JVM really doesn’t support this feature.

I think that’s the important thing to keep in mind for a lot of language decisions targeting the JVM (or any platform for that matter). It can be done, it’s just a matter of how much is faked or forced in by the language and how much is built into the platform.

Dynamic properties aren’t directly supported by the JVM, but they can be simulated by a language - the JVM doesn’t have built-in support for extension methods or sealed classes either, those are handled by the compiler.

The big question is whether it’s a good idea - or good enough to justify the work needed to implement it at least. Personally I’m very much against the idea myself; one of the things that put me off of vanilla JS back in the day was not being able to know ahead of time what properties were available - and if I’m adding something, who’s to say I’m not overwriting an existing property needed some where else?

1 Like

Ok, I haven’t studied the implementation, so you may be correct. In my reply, I said “The JVM allows it.” which is correct, and that addresses the issue raised by the OP. Whether that means the JVM allows it or supports it is a semantic question, and those can be argued interminably.

BTW, I’m not well-versed in Kotlin, but I see this statement in the Kotlin documentation for extensions (https://kotlinlang.org/docs/reference/extensions.html):

val Foo.bar = 1 // error: initializers are not allowed for extension properties

My examples shows this is allowable in Groovy Expando. So the concepts are similar but not exactly the same.

I’m a bit confused.
Are we discussing dynamically extendable classes like Groovy’s Expando?
Or are we discussing dot access to values of a map?
(Or some mix of both)

The reason I mention the second is that OP seems to want something similar to Groovy’s dot access to values in a map:

For example:

val someMap = mutableMapOf("firstname" to "Donald")

someMap["lastname"] = "Duck" // using set operator
someMap.lastname = "Duck" // using proposed dot access

Dot access is available in Groovy.

Personally, I don’t like Groovy’s dot access. I find it is often abused by “Map-ly” typing classes that are clearly structured data.


Right now you can delegate a property to a map, which may help OP if you don’t want to use the getter operator function, someMap["key"].

fun main() {
    val someMap = mutableMapOf("firstname" to "Donald")

    var lastname by someMap
    lastname = "Duck"

    println(someMap)
}

That is not correct. Kotlin dispatches extensions statically, while Groovy Expando is a dynamical construct. The call of undefined property just has default fallback to propertyMissing or getProperty methods. The construct has a lot of uses, but it sacrifices safety and tooling support to achieve it.

The closest thing kotlin has to it is a by Map delegate. It allows to convert property name to map key. Still, it could not be adjusted dynamically.

2 Likes

I think that best way to emulate dot access to values of a map today is to write your custom property delegate like this:

class MutableProperty<T>: ReadWriteProperty<MutableMap<String, T>, T> {
override fun getValue(thisRef: MutableMap<String, T>, property: KProperty<*>): T = thisRef.getValue(property.name)

override fun setValue(thisRef: MutableMap<String, T>, property: KProperty<*>, value: T) {
    thisRef[property.name] = value
}

}

Then declare properties that you need and use them anywhere in your code:

var MutableMap<String, String>.lastName by MutableProperty()
var MutableMap<String, Any>.anyProp by MutableProperty()

If you need the same property access on immutable map you should implement ReadOnlyProperty delegate too and declare same property for both map interfaces:

val Map<String, String>.lastName by ImmutableProperty()
var MutableMap<String, String>.lastName by MutableProperty()
    @JvmName("get_MutableLastName")  // this needed to resolve platform declaration clash

But I believe this approach adds some computation overhead to map access.

It would be great if Kotlin has a feature that would allow us to declare properties like this:

val Map<String, String>.firstName by this   // also accessible on MutableMap as readonly property

var MutableMap<String, String>.lastName by this // also accessible on Map as readonly property

And this properties should be compiled to the same bytecode as simple map access in the place where it is used:

val firstName = myMap["firstName"]