10
data class House(var name: String = "House", var door: Door = Door())
data class Door(var name: String = "Door")

fun test() {
   val testHouse = House("My house", Door(name = "My door"))
}

How could I get nested property reference nice and safe, ideally like this (this doesn't work though):

   val houseDoorName = House::door::name
   println(houseDoorName.get(testHouse)) //My door

I figured I could maybe do extension function, something like: House::door.nested(Door::name) but Im stuck with the implementation.

4
  • Why won't you just do testHouse.door.name? Commented Jun 25, 2017 at 21:42
  • 1
    @Mibac I'm experimenting with data binding: csv columns to properties, so I need to use reflection / functional style. Commented Jun 25, 2017 at 21:49
  • @nukle a csv format is more like a MutableMap<String,MutableMap<Int,Any>>. why did you need to use reflection rather than csv[column][index] = "foo" ? Commented Jun 25, 2017 at 23:17
  • 2
    @holi-java Yes, however I'm experimenting with an alternative way of doing that. Your style would then require the client to loop the rows and columns and map to beans like that, right? I'm trying to do something like val mapping = listOf(House::name toCsvColumn "house name", moremappings...); val listOfHouses = parser.read(House::class, csvFile, mapping) for providing declarative way of doing that. More than anything else, this is just to have fun with Kotlin :) Commented Jun 26, 2017 at 5:06

1 Answer 1

13

For your hypothetical nested function, try this:

fun <A, B, C> ((A) -> B).nested(getter : (B) -> C) : (A) -> C = { getter(this(it)) }

Now you can do exactly what you asked:

val houseDoorName = House::door.nested(Door::name)
val house = House(door = Door(name = "My door"))
println(houseDoorName(house)) // prints "My door"

You can chain it, too:

val doorNameLength = House::door.nested(Door::name).nested(String::length)

The neat trick here is the way Kotlin allows a property reference to be treated as a function.

The nested function is essentially a functional composition. It takes a function a -> b and a function b -> c, and composes them into a new function a -> c. You'll often find it called compose in standard libraries.

Kotlin doesn't have function composition as standard, but there are libraries out there if you need anything more complex than this.

Sign up to request clarification or add additional context in comments.

4 Comments

This kind of answers my question, thank you. However I realize that I would like that nested function's return type would be KMutableProperty1<A, C> and parameter type would be KMutableProperty1<B, C>, because I might need the setter as well.
@nukle If you are ever to make that, I suggest you to create your own class as result type, like data class GetterAndSetter<T, R>(val getter: (T)->R, val setter: (T, R) -> Unit). It would be rather bad to mess with KMutableProperty because the result no longer represents a property.
@glee8e Yes that is probably the case, I will try that. Thanks.
Neat. If you make nested an operator fun called invoke, you can chain it like this (House::door)(Door::name). Rather obscure, but succinct if you need to chain often. The first parenthesis is necessary, otherwise I get an error "This syntax is reserved for future use; to call a reference, enclose it in parentheses: (foo::bar)(args)".

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.