Mutable vals, yay or nay? I was working with this...
# getting-started
r
Mutable vals, yay or nay? I was working with this interface:
Copy code
interface FooInterface {
  val x: X
}
I introduced a bug, stemming from me only reading x once, because I thought “val means immutable”. The bug fix was to read it multiple times, since it changes. How does it change? Well, in interfaces,
val
apparently only means “no public setter”, it does not mean “immutable”. The implementation looks something like this:
Copy code
class Foo: FooInterface {
  override val x: X
    get() = something.getX()
}
Now, my gut reaction to this was “This is awful, ‘val’ should mean ‘immutable’, the interface should change from a field to a getter function to make it more clear.“. However,
val
in interfaces meaning “no public setter” and not meaning “immutable” is how the language actually works. So should I rewrite code so that by convention, “val” means “immutable”, or would that just bite me later? Would you change the interface/implementation?
y
val
just means "has a getter" and that's all. In fact, you can override a
val
property with a
var
just fine. Note that, even if the implementation used a field,
X
itself could be mutable in some way. What you want is some deep immutability, which is a complicated language addition. I wouldn't change the interface at all personally. I might instead add a doc-comment to say "x should return the same value always" or something.
r
Thank you for your answer! Just to clarify, it should NOT return the same value always in this case, you should call it multiple times to always get the up to date X. Also, X is not internally mutable in some way, it’s a data class, conceptually. Am I wrong for wanting something to signify that a variable is immutable? I noticed that Jetpack Compose has @Immutable, I guess because val can’t be trusted?
y
Let's clear up some terms here: I believe Compose's
@Immutable
applies to types, and signifies that all their properties are themselves immmutable (with the base case being primitives). Wanting something like that is normal, it just is likely complicated to bring into the language (e.g. you can't trust
List
, or most interfaces really). In your case,
val
is perfectly reasonable to use. I guess you could also change it to a
fun
instead to hammer home that it really should be called to guarantee you're up to date. However, I'd recommend using
val
here, since `val`s are generally "idempotent", and that seems to be the case with your property.
r
@Youssef Shoaib [MOD] I don’t undestand what you mean by this:
I guess you could also change it to a
fun
instead to hammer home that it really should be called to guarantee you’re up to date. However, I’d recommend using
val
here, since `val`s are generally “idempotent”, and that seems to be the case with your property.
This sounds like a contradiction. In my case it’s mostly idempotent, as in if you call it 10 times in a row, it will probably return the same X every time, but the point is that it is NOT idempotent and I introduced a bug by only calling it once. I was thinking “`val` if idempotent,
fun
if you should call it every time to get the up to date value”, but that would just be a convention. What do you think about
val nextRandomInt
? Is that fine, because that’s how the language works, or should there be some style guidelines against it, in your opinion? Are there any official recommendations for vals vs funs? The way I understand it, they are completely interchangeable (functionally), so maybe the distinction would be purely by convention?
y
Yeah I think I'm using the wrong term. I guess I mean that
val
should only have read-like side effects, and so if the source hasn't changed, calling it multiple times should result in the same value. If you then call other functions afterwards, then it's on you to ensure that those functions don't change the source.
val nextRandomInt
I think I'd be completely against because calling it has a side effect that permanently changes something else.
r
What do you mean by read-like side effects? Also, in my case, x changes by itself, independently of what my code is doing. I.e. if I called it 10 times in a row, I might get A the first 7 times and then B for the next 3.
So, you’d disagree with
val nextRandomInt
, because it would supposedly change the internal state of a random number generator somewhere? What do you think about
val currentTimeInMilliseconds
?
y
currentTimeInMilliseconds
I'd be fine with. Calling it repeatedly does nothing of value. Calling it, and then calling some function, may render it out of date. By "read-like side effects", I basically mean that, if the value of a property call is ignored for some reason, then removing the call shouldn't change the behaviour of code. There are exceptions here of course for DSLs and such
r
Okay, so in summary, I understand it like this: • There are ways how the language works, e.g. reading a val multiple times can give different results and reading a val can basically execute any code, mutating state somewhere • There are opinions on which of these features should actually be used in code and which should be discouraged, e.g. @Youssef Shoaib [MOD] thinks that reading different values from the same val can be okay, but changing state by reading is not okay Thank you for the discussion, it was really helpful; in the absence of any official recommendations, I’ll go with my original “val should mean immutable” opinion and discuss with my colleagues if we want to adopt it as a style guide.
e
you may be interested in watching https://youtrack.jetbrains.com/issue/KT-27325
thank you color 1