Challenges in Building for Fluency

Building fluency into code takes some effort. Explore if an extension function will be useful. Sometimes an infix method may help remove some clutter in code—to get rid of the dot and parenthesis. Maybe an implicit receiver will come to the rescue. You can choose from a number of techniques, and sometimes they may appear to conflict with one another. Let’s look at an example of this situation.

To use the infix notation, so you can remove the dot and parenthesis, you need an object reference. For instance, a person reference is needed to use infix notation to make the following fluent: person run "fast". But by using an implicit reference to the instance of this hypothetical Person class, we can get rid of the object reference in the call and write only run("fast"). Wait, it appears that we can either get rid of the dot along with parenthesis or we can dismiss the object reference, but not both. One work-around would be to use this instead of person since the implicit reference can be explicitly referenced using this. But writing this run "fast" doesn’t appeal to my fluency- demanding pallet. What gives?

It’s easy to get discouraged when we run into conflicts like this. But by step- ping back to the basics, we can think through ways to arrive at a working solution. Let’s devise a solution: to create a fluent syntax without dots, parenthesis, and this.

Using extension functions

Suppose we’re creating an application that will keep track of events and dates. Maybe we want to mention that some event happened 2 days ago and another event will happen maybe 3 days from_now. With some extension functions, we can make the words roll off our tongues and emerge as code—Kotlin code, that is.

We’ll start with the code to extend the Int class with a days() method to facilitate the fluent domain-specific method call:

// DateUtil.kt
package datedsl

import java.util.Calendar 
import datedsl.DateUtil.Tense.*

infix fun Int.days(timing: DateUtil.Tense) = DateUtil(this, timing)

The newly injected days() method takes the yet-to-be-written DateUtil.Tense enum and returns an instance of the DateUtil class, also yet to be written. The import statement brings in the values of the enum into the scope for easy use with the when argument matching we’ll use soon.

The DateUtil class will take the number, the Int instance on which days() was called, and the given enum and take the necessary steps to return the appropriate date for the hypothetical event. Let’s take a look at the DateUtil class now.

Get hands-on with 1200+ tech skills courses.