Unary operator for parameter-less get


#1

I often get frustrated with the lack of overridable unary operator for a parameter-less get.

I mean, cont[42] will get translated to cont.get(42) but there are no operator that translates to cont.get().

Instinctively, because I come from C++, I would use the operator * and have *cont translated to cont.get(), but in kotlin, the * is the spread operator.

So I ended up overriding the unaryPlus operator, but somehow +cont does not seem very semantic. To me, +cont has, above all, an arithmetical semantic. That’s what I liked about *cont syntax: because it is not valid arithmetic, its semantic is more easily understood.
Furthermore, I’d like an operator that translates to cont.get(), not cont.unaryPlus().

Since cont[a, b] is valid in Kotln, maybe the solution is cont[]. I don’t really like it though, I’d prefer a unary prefixed operator.
Lots of candidates are available:
%cont
^cont
#cont
~cont
&cont
¯\_(ツ)_/¯cont

The use case is very simple: Every time I get a container class that contains one value to add some information (you know, composition over inheritance).
This is an example:

class Counter<T>(private val _obj: T) {

    var count: Int = 0
    private set

    operator fun get(): T { // Operator!
        ++count
        return _obj
    }

}

Also, all Atomic... classes are good candidates. I’d rather read/write println("Current value: ${#count}") then println("Current value: ${count.get()}") (provided that count is an AtomicInteger).

What do you think ?


#2

I strongly believe, the current design actually is a feature. a[] would be ok for get() as it seamlessly builds on the current concepts.


#3

Kotlin is not big on unary operators (prefix or suffix). Here is what I think you would typically see in Kotlin:

class Counter<T>(value: T) {
    var count: Int = 0
    private set

    val value : T = value
        get() {
            count++
            return field
        }
    
    operator fun invoke() = value
}

Which you can then get the value using either of these:

myCounter.value
myCounter()

#4

@Dittert Could you give the reason of why ?

@dalewking Well, this solution troubles me for not being semantic. I see a function call, not an access to an enclosed object.
cont["name"] is really semantic: one can instantly understand that it means "Accessing the object that the variable cont is holding under the key "name"". The real type of cont, whether it’s a Map or any other type of container does not come into account.
cont["name"] is readable, semantic and instantly understandable. I would consider it bad practice if it would do any kind of heavy computing, because I do not consider it a function call, but a value access. Of course it translates to a function call, but it does not read as one.

Personnally, I like #cont because I can easily associate the semantic of cont.get() to this syntax (once again, coming from C++), but any kind of postfix operator would also do :slight_smile: .


#5

Thinking about it some more, I think the problem is that you are trying to approach this from the wrong angle, trying to think of it in terms of having an object and applying an operator to it. My solution was sticking with your “the only tool I have is a hammer, so this problem is a nail” approach.

Lets step back and focus on what you are trying to achieve rather than the way you are trying to achieve it. As I understand it you have some value (we’ll call it X) that you want to expose via a property called cont and every time you read the value via cont you want to increase a counter.

A more general description of the problem is that you have some value of some type T and you want to expose it via a property and to be able to perform an action whenever the property is read.

The focus should be on the property. Kotlin has a way to do that which Java and C++ don’t have and it is via property delegation

So a mechanism for performing an action every time a value is read via a property might look like this:

fun <T> T.readObserver(onRead: KProperty<*>.(T) -> Unit)
    = object : ReadOnlyProperty<Any?, T>
    {
        override fun getValue(thisRef: Any?, property: KProperty<*>)
            = this@readObserver.apply { property.onRead(this) }
    }

which could be used like this:

val myProperty by someValue.readObserver {
    println("property $name whose value is $it was just read")
}

Then to apply that to the specific scenario you talked about we could do:

private val X = "The value you are trying to expose through cont property"
private val readCounter = AtomicInteger()

val cont by X.readObserver { readCounter.getAndIncrement() }

There I did it without using ANY operators at all.

Kotlin has a standard delegate like this for writes to a property, but not reads.

var foo by Delegates.observable(X)
    { property, old, new ->
        println("property ${property.name} was changed from $old to $new") }

#6

I don’t agree.
I was really talking about classes that are containers that add smantic to an enclosed property.
AtomicInteger is an example, WeakReference is another. My Counter class is yet another example.

Those classes uses are through the get function most of the time. Because “multi-object” containers do have an operator to cont.get(key): cont[key].

Let’s take WeakReference. For most of it’s usage, you’re going to call get(). It hink calling myFunction(#ref) or even myFunction(ref[]) is more understandable than myFunction(ref.get()) for these reasons:

  • It reads as an access, not a function call, which is really it’s underlying semantic.
  • It emphasizes the fact that ref is a container whose primary purpose is to contain a value, and add some semantic regarding that value.

Now regarding your solution, I don’t like it. It’s very personal so don’t take it the wrong way :wink: It’s perfectly valid, but not how I would have written it.
Here are the reasons:

  • It only works for properties. I must be able to have a container as a variable. I can’t pass the container as a variable. I must be able to call myFunction(ref) or myFunction(#ref) whether myFunction takes a WeakReference<T> or a T parameter.
  • Any “added semantic” property is along the “main” property. In your example, There’s X, there’s readCounter and there’s cont. Three properties (two of them have backing fields), all three alongside probably other properties inside the object when all I really wanted was one property, on which I will use get() most of the time, and getCount() once in a while. I don’t like having readCounter alongside cont because readCounter is related to cont. It’s effectively a property of cont, not of the englobing object.

#7

After working with it some more, you don’t even need the property delegate, you can do the same thing with custom getter:

private val X = "The value you are trying to expose through cont property"
private val readCounter = AtomicInteger()

val cont = X
    get() = field.apply{readCounter.getAndIncrement()}

#8

So I read your response mostly as “I don’t like your solution because I can’t hit it with my hammer, which is the only tool I have and know how to use”

X is just a property here just for simplifying the illustration by giving a name to the value. it is just a value and could be inlined in the example. So lets do that using my revised code that got rid of the delegate:

private val readCounter = AtomicInteger()

val cont = "The value you are trying to expose through cont property"
    get() = field.apply{readCounter.getAndIncrement()}

I could actually get rid of the backing field on cont and put the value into the getter, but then I would have had to specify the type for cont in the example

You have to store the state of the count somewhere so that can’t be eliminated it will be a property somewhere. The only real question here is how someone reads the count of accesses. I was assuming that the object that declared the cont property was the only one that needed the count so I stored it there. Feel free to move it somewhere else.

To me the question is how do you get count of accesses of the property. Is it an attribute of the property or is it separate from the property. If the latter then I already showed the solution for that. If the former then you have to a way to say you want the access count for the property or you want the value of the property. In that case you need to quit thinking of the property as thing that has a value, but instead it should be viewed as a function that produces a value, Don’t think of it as an object that you call get on, it is a function you can invoke to get a result. So this would be the appropriate solution:

class CountedFunction<T>(private val producer: () -> T) : () -> T
{
    constructor(value: T) : this( { value } )

    val counter = AtomicInteger()

    val invocationCount: Int get() = counter.get()

    override fun invoke() = producer().apply { counter.getAndIncrement() }
}

val cont = CountedFunction("value to return")

fun foo()
{
    val x = cont();
    println("cont was invoked ${cont.invocationCount} times")
}

I realize this is not the way you normally think about it since you probably are not use to functional programming.

#ref carries no meaning and makes me think someone is trying to write Perl. I have no objections to having the operator overloading allowing a no parameter version of get to allow ref[] although I don’t think this is an intuitive use of [].

Once again, what is a WeakReference in a functional programming mindset, it is a function that you can invoke to get the value (or null). So the solution is

inline operator fun <T> WeakReference<T>.invoke() = get()

Then I can use function call semantics on any WeakReference as in

val x = someWeakReference() // Result will be nullable

Good luck convincing anyone that #ref is a better choice than ref()

No idea what you were trying to say here


Access to delegate object
#9

Sorry for my bad english.
I meant that most usage of a container is access and set. For example, most usage of a map will be map[key] and map[key] = value.

As for your comment "So I read your response mostly as “I don’t like your solution because I can’t hit it with my hammer, which is the only tool I have and know how to use”, please don’t put words in my mouth.
My real motive is “There’s a tool I feel is missing, I’m proposing it”. Just. That.
If I would do phrase your solution the way you phrased my request, I would say “I have a nail to hammer, but I don’t have a hammer, so let’s use a brick instead, you can very well hit the nail with it”.

I don’t see an access inside a map as a function, and I don’t believe kotlin does either . If it did, then we would use respectively map(key) and map(key, value). Or even map(key)(value).

The example of #cont is just that… an example. Any operator whether prefix or postfix would do. It’s just my personnal favourite, feel free to replace it with the syntax of your choice.

So, once again: we already have operators that translates to parameterized get.
What I would like is an operator that convey the same “accessing enclosed” semantic, just like the [key] operator that kotlin has, but without a parameter. In essence, the semantic of the unary operator * of C++.

I mean, it seems completely illogical to me to have an operator that converts to get(key), and none that converts to get().
cont() has a semantic, and it is not that one.
I have done some pure functionnal programming, and I’m not a fan. Kotlin is by no means a functionnal language, it has functionnal features (which I love), but it’s far from a functionnal language (which is also why I love it).

I don’t believe cont() has the same semantics as cont.get(). There’s a reason why cont() translates to cont.invoke(). Because it conveys the semantic of a function invocation, not an access.
To me, overriding an operator to convey a different semantic than the “regular” use of this operator is a very bad practice.

In this instance, the return of cont() should be the result of a computation, represented by cont.


#10

Certainly. Developers spend a lot more time reading code than writing code. As a consequence, code should communicate intent as clearly as possible. To be able to do that, you need to use proper vocabulary to communicate. This is why functions, classes, variables, and objects have names. That name should be as descriptive as possible to avoid any misunderstandings (e.g. that is why a range has an endInclusive and not just b that could mean bound).

We have all been trained to have a common understanding of some abbreviations, though: == has to to with equality, [3] has to do with accessing the third element of something, and so on. This is why I strongly believe that every one of those abbreviations needs to have a strong justification to exist. The more hard to grasp meanings such operators can have, the more difficult it will become to understand code. Especially if different projects associate distinct meanings with operators.

I wouldn’t even be able to imagine what ¯\_(ツ)_/¯ would do as an operator but disambiguateAbbreviations() would help a lot (if I knew a little about the context). I strongly believe, that this is not something Kotlin should try to achieve.

Don’t get me wrong, I do understand what you wanted to say with those examples. But even the more pragmatic examples are open for interpretation: I would rather associate #cont with cont.size() than with cont.get() for example.

This is why I also said that [] could be ok for get() because it is a simple extension of the concept that is behind the idea of "[i] means get(i)" rather something completely new.


#11

I don’t see it that way either, it is more like array indexing which is why Kotlin provides the same operator for it and why they didn’t allow you to define the operator without any parameters like you were suggesting. But I am not sure how maps got in here as I have not mentioned them at all. What would be the meaning of your mythical # operator on a map?

I did, the correct way to handle it is custom getter for the property or as a function.

The get of map and list and the get of something like weak reference are logically 2 very different concepts that just coincidentally happen to be mapped to the same method name in Java’s non-functional world.

For things like map, arrays, and lists, the get is an indexing operation, which say to retrieve a particular item in the collection. Java called it get since it has no operator overloading. That is why in Kotlin, they require a parameter, since it make no sense to index a collection based on nothing

Parameterless get methods on things like weak reference are really because the object is function object (sometimes called a functor), but since Java does not have first class functions they had to resort to a parameterless get method.

In a language that supports functional programming like Kotlin the proper representation of these are with indesing operator [ ] for the map and list case, and for something like WeakReference to model it as a function. It would actually be nice if Kotlin automatically mapped parameterless methods named get to the parameterless invoke operator (and they can do the same for methods named call with any number of parameters), but until then you can do it yourself with extension functions as I already demonstrated.

I don’t want to get into a definition war over what it means to be functional. Kotlin is functional in that functions are first class citizens and we have higher order functions etc. It is less functional than Scala or Haskell, but on the other end of the spectrum Java is completely not functional and its API was designed that way, so when you have something like a WeakReference that really is a function object you map it to a parameterless get method. In a functional world you would have designed it as a function.

You may believe that, but you are wrong. You have this object and you want to tell it to return a value to you, that is pretty much the definition of a function object.


#12

For the record if there were a need for a custom prefix operator the most likely candidate is * since Kotlin already uses it as a prefix operator for the spread operation.

For completeness sake I checked the Java 8 docs for how many places a parameterless get is used and here are the only cases:

  • Implementations of java.lang.ref.Reference, including Soft, Weak and Phantom references.
    • I’ve already made the case for function invocation semantics
  • java.lang.ThreadLocal
  • Implementations java.nio.Buffer
    • If anything this get() makes the most sense as a post-increment operator
  • java.rmi.MarshalledObject
  • java.util.concurrent.atomic.AtomicXXXXX classes
  • implementations of java.util.concurrent.Future
    • These DEFINITELY are function objects and are basically functions that can start running before you call them
  • implementations of javax.naming.directory.Attribute

I don’t see a very strong case for your operator in this list. Several are better mapped as functions or as things like a property named value (e.g. myThreadLocal.value)


#13

@Dittert Thank you :wink:

I strongly believe it to :wink: Operator overuse and misuse is so easily achived…

I also agree to this. However, the meaning we associate to operators changes between languages. The fact that * is the spread operator, for example, is not a language wide consensus. Nor is the “companion object”. Those are specific to Kotlin and we had to learn their semantic, whatever the language we came from.
How exactly to balance “We need an efficient syntax”, “We must be specific” and “We must be easily undestandable” is a matter of taste, I think. Kotlin is very “conservative” in that regard (which we all agree is a great thing), but it does have, now and then, its own peculiarities :wink:

That would certainly work for me :slight_smile: