Properties private to functions

Would it be possible to declare properties (with lifetime bounded by class lifetime) but nested inside a function and only accessible there. If the property is only used in one function then this would both make it obvious, and avoid it being ‘misused’ elsewhere.

eg

fun foo() {
. private var counter = 0
. counter++
}

This would be even more useful when synchronising on lock objects as it is obvious that only the current method uses the lock object and helps to prevent further edits creating deadlocks.

fun restrictedAccess() {
. private val lock = “restrictedAccessLock”
. synchronised (lock) {
. }
}

3 Likes

All val/vars are private to functions. And you do not have to think about lifetimes since it is handled by the garbage collector.

True, but I am interested in a property whose lifetime is longer than the function within which it is declared. For example the logic for fun restricted access is currently written

private val lock = “restrictedAccessLock”
fun restrictedAccess() {
. synchronised (lock) {
. }
}

Which means that the val ‘lock’ can get separated from the function and misused, possibly causing deadlocks.

Writing the code as this:

fun restrictedAccess() {
. val lock = “restrictedAccessLock”
. synchronised (lock) {
. }
}
would mean that the lock was a new instance every time resulting in no effective lock.

If the value is visible to somebody, it will exist indefinitely. If it is not visible, it will be cleaned.

No you cannot have a class member which is visibile only inside a function.

You can create another object Bar which privately contains the lock and the code of restrictedAccess, and have a Bar as a member of your class.

Yes indeed, that is the current situation, I am asking about a possible language change…

At first sight, it looks good, but once I think more about it, it raises lot of question.

For example, in this case:

fun doStuff() {
    private var counter = 0 
    counter++
}

When is this property initialized ?

  • At parent object initialization → can make object state initialization hard to follow
  • When first calling the function → But then, a how to handle concurrent calls to the function ?

Also, regardless or val/var :

  • How to attach documentation to such a field ?
  • For maintainers, it can become quite difficult to trace all properties maintained by an object.
4 Likes

I think it relates a bit to methods as owner of other methods and to static vars in C.

But did you have a compelling use case, I don’t quite understand yours, why not just include the property as local value into restrictedAccess.

It would be nondeterministic for concurrent execution.

I think you wouldn’t do that as function content is implementation detail

Can you elaborate a bit more, please?

Because the value may need to persist across invocations of the method… for example locking on a new object each call will not enforce single thread access…

2 Likes

“initialisation guaranteed to have been performed before the first read of the value”? I am not a compile writer, so I do not know what that implies :wink:

I would like to believe that it is just an instance property (initialised at class initialisation time) but that can only be referenced from one function/block/scope.

Consider a functional typed property:

    val restrictedAccess = object : () -> Unit {
        private val lock = Any()
        override operator fun invoke() {
            synchronized(lock) {
                println("locked")
            }
        }
    }

If you’d prefer the API to have an actual method, you could just use private val _restrictedAccess = ... and wrap that with a fun restrictedAccess method.

If you use that pattern often you could always create a helper method:

    fun selfSynchronizedInvocable(block: () -> Unit) = object : () -> Unit {
        private val lock = Any()
        override operator fun invoke() {
            synchronized(lock) {
                block()
            }
        }
    }

    val restrictedAccess =  selfSynchronizedInvocable { println("locked") }
5 Likes

Thanks for the suggestion. That certainly looks interesting. I guess that I would simply invoke by:

    restrictedAccess()

to print ‘locked’?

In the absence of language support then that provides a more robust solution than leaving the lock object floating around the class definition. It does require more ‘boilerplate’ than my suggested change to the language though. It might be that my use case is not common enough for a language change.

I had thought that the difference between

class Foo {
    private val lock = Any() // visible within class
    fun bar() {
        synchronized(lock){
            println()
        }
    }
}

and

class Foo1 {
    fun bar() {
        private val lock = Any() // visible within method
        synchronized(lock){
            println()
        }
    }
}

would have been quite simple - but I am not a compiler-writer. ‘lock’ would still be a property on the instance of the class treated exactly as normal instance properties The only difference would be that access would be restricted by the compiler.

1 Like

I don’t see this making beyond the “-100 points rule”.

This use-case is very speficific to ‘a class a memeber that needs to be read by a single method’.
Now kotlin has solutions for that, either defining a new object with encapsulates the logic or what @nickallendev has suggested, which is a way to define that inline.

As soon as you need 2 class methods to ‘see’ this memeber you need to walk back from using static and revert to the “define a new object” approach.

Certainly it is soluble - and I am grateful for the suggested solution. (And grateful for Kotlin too)
Excuse my ignorance, but what is ‘the 100 points rule’?

I’m not sure what’s the original reference I found this: Minus 100 points.md · GitHub.

Basically the idea is that to add a new feature to a language you don’t just need a positive impact, but you need to weigh the cost of having an additional feature as a whole, and that is what these “-100” points are.

So a feature to make the cut needs to be valued more than “100 points”. Note that there is no definition of point.

2 Likes

Thanks :+1:

I’d love to see this feature. While I agree with the issues mentioned above, this comes up in the code so often that it definitely beats the minus 100 points things for me.

I have never felt the need for this, if you have state that is local to one (or few) operations, you can simply create a class for it. That’s how you improve cohesion.

4 Likes

I think it is harder to communicate this approach than wrapping the function and the value in one object.
Because functions didn’t have any fields, private and static won’t communicate enough about the presence of a field.