how can I rewrite code that looks like this to be ...
# announcements
r
how can I rewrite code that looks like this to be more concise so that I avoid having to mention foo.someProperty twice?
Copy code
fun computeNewValue(msg: String, oldValue: String): String {
    return try {
    	println(msg)
        computeNewValue()
    } catch(e: RuntimeException) {
        oldValue
    }
}

fun main() {
    val foo = Foo()
    foo.someProperty = computeNewValue("Computing foo", foo.someProperty)    
}
n
you can remove the println /s
for your first fun you can use expression syntax to save a line, but besides that I don't think there's too much you can do
r
lets say I have a bunch of these, and computeNewValue is a generic function
Copy code
private inline fun <reified T> read(key: String, oldValue: T):
then I have to write
Copy code
foo.property1 = read("property1", foo.property1)
foo.property2 = read("property2", foo.property2)
Maybe using
KMutableProperty1
I could do something to the signature of
read()
https://kotlinlang.org/docs/tutorials/kotlin-for-py/member-references-and-reflection.html
n
you could implement your own delegated property
it's kinda heavy, but maybe just what you want
h
IMHO if you would be able to only mention
foo.someProperty
once, it would be akin to augmented assignment. nothing wrong with that in general, but it would be yet another step away from immutability. i feel it would fit better in a language where references to mutable variables occur naturally, e.g. c++. for languages such as kotlin or rust, i would try to do something else
r
This seems to compile lets try it
Copy code
private inline fun <reified T> read(prop: KMutableProperty0<T>): T {
    return read(prop::name.get(), prop.get())
}
and
Copy code
private inline fun <reified T> read(key: String, oldValue: T): T {
now I can write
Copy code
foo.someProperty = read(foo::someProperty)
n
wasn't the point to avoid writing someProperty twice?
r
hmm ideally yes. I got rid of the “key” parameter which is just the string name of it.
I’m trying a delegated property
I guess a delegate wrapper would work
t
I'm still not sure if this is something you should do, but this would be a generic way to avoid having the property name twice, using reflection:
Copy code
fun <T> KMutableProperty0<T>.refreshValue(msg: String) {
    try {
        println(msg)
        set(computeNewValue())
    } catch (ignored: RuntimeException) {}
}

fun main() {
    val foo = Foo()
    foo::someProperty.refreshValue("Computing foo")
}
But before using that, I'd consider restructuring the whole thing. Maybe instead of throwing an exception you could return
null
(or at least do so in the wrapper function) and then only assign it if the value is not
null
(
computeNewValue()?.let { foo.someProperty = it }
)
t
I don't think so. The possible exception happens before the value is assigned, so if it is thrown, the delegate is never called.
n
ah, yeah.
r
I have now
Copy code
private inline fun <reified T> update(prop: KMutableProperty0<T>) {
    val newValue = read(prop::name.get(), prop.get())
    prop.set(newValue)
}

private inline fun <reified T> read(key: String, oldValue: T): T {
 
Which I call using

update(foo::someProperty)
This is bad I guess since it messes with “find usages”. The
let
solution is perhaps cleanest in that sense
n
How is augmented assignment a step away from immutability?
I think Tobias suggest is probably the cleanest, no reason to pump the old value in there
an alternative is to use something like
%=
, if you only define
%
then when you do
x %= foo
it will turn that into
x = x % foo
i'd only do that as part of a DSL though, not a one off
t
That was actually my first thought too, but this will look terribly confusing to anyone else who reads it. Also, what would you use as the parameter? the message?
n
yeah I suppose.
But yeah, that's why I wouldn't use it as a one off.
I do want to suggest something like that for arrow.lenses though, in the context of the broader kind of DSL I think it is worth it
t
sure, there are cases where it makes more sense. I also like overriding the div-function for building uris/paths (like
basePath / "myDir" / filename
)
1
n
yeah, I like that one a lot too
python and C++ both have it.
So nice that kotlin lets you do it as an extension function, so you're not dependent on the original library author
(like C++ but unlike python)
r
the
let
solution fails because it cannot infer the type… I had
Copy code
private inline fun <reified T> read(key: String): T
But of course I cannot write
Copy code
read("foo")?.let { repository.foo = it }
n
you could pass the type explicitly
but obviously, that's still something you'd rather not pass
@rrva hard for me to test this but what if it took a lambda as well
Copy code
private inline fun <reified T> read(key: String, setter: (T) -> Unit)
and then you called it as
read("foo") { repository.foo = it}
I'm not sure exactly how type inference works out in this situation
r
this worked:
Copy code
private inline fun <reified T> read(prop: KMutableProperty0<T>): T? {
    return read(prop::name.get())
}

private inline fun <reified T> read(key: String): T? { ... }

read(repository::someProperty)?.let { repository.someProperty = it }
n
Anyhow, in this situation,
read
would internally only call
setter
if it got a new value
Yeah, but now you're duplicating just as bad as originally 🙂
r
ok lets try the lambda route
n
Try my solution?
looks like no cigar
r
yeah, type inference has nothing to go on
n
well, that's not true
the type inference could work easily, it just doesn't
repository.someProperty = it
is enough to deduce the type of
it
r
well yes, but the assignment does evaluate to the type?
n
I don't think I understand. I'm not sure if assignment is an expression in kotlin. but the point there is that kotlin doesn't have implicit conversion
so if you know the type of the left hand side, then
it
must be the same type
Rust and Haskell have this kind of type deduction, you can do things like
val x = list(); x.push(5)
and it will go "backwards" and deduce that x must have been list<int>
similarly here, it could go backwards and conclude that
it
must be whatever type, to make the body of the lambda work. ANd then, go further backwards to deduce the correct types for
read
Anyhow though this doesn't help you 🙂
The thing I know will definitely help you is to use an operator like
%=
but it's pretty obscure
you could end up with syntax like
repository.someProperty %= read("hello")
can use
%=
,
*=
, etc, doesn't matter which one