Challenges in Building for Fluency
We'll cover the following
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.