Diving deeper into context-oriented programming in Kotlin

Last updated 2 years ago by Alexander Nozik


This article is a follow-up on my previous article on context-oriented programming (I was quite delighted to find that some guys in Kotlin community started to use abbreviation COP for it already). This time I want to go deeper and show a more complicated real-life example of the same approach.

Context-scoped functions (mapping)

Here is an example that appeared in a discussion about custom mappings. One quite frequently wants for some kind of behavior to be added to the existing class. That is what extensions for:

fun Int.map(): String = toString()

But seldom one wants this behavior to be different in different places. For that, we usually define an interface and few instances like :

interface IntMapper{ operator fun get(index: Int): String }

object DefaultMapper: IntMapper{ override fun get(index: Int) = "NONE" }

object MyMapper: IntMapper{ override fun get(index: Int) = index.toString() }

And then use it wherever we like:

val i = 10 val str = DefaultMapper[i]

It solves the problem in most cases, but it does not, in fact, add behavior to a class. One needs to explicitly call the mapper object each time and it is not what we need. So, since we need some functionality to exist in a context, let us make it context bound:

interface IntMapper{ fun Int.map(): String }

object MyMapper: IntMapper{ fun Int.map(): String = toString() }

and then:

MyMapper.run{ val i = 10 val str = i.map() }

In Kotlin the context is even not necessarily local, one can pass it to some external function, declaring it as a receiver:

fun IntMapper.doSomethingWithMap(){ val i = 10 val str = i.map() }

MyMapper.run{ doSomethingWithMap() }

In fact, we can do even better and avoid interfaces completely:

object MapperScope

fun doSomething(){ MapperScope.run{ fun Int.map(): String = toString() val i = 10 val str = i.map() } }

In this case, mapping function will only exist inside one specific scope and will never leave it.

Read full Article