1. Introduction
In this tutorial, we’ll discuss the Pimp My Library Pattern in Scala. This is a very simple yet very powerful concept that adds a dimension of flexibility to Scala programs.
2. What Is Pimp My Library Pattern?
This is just a fancy expression to refer to the ability to supplement a library using implicit conversions. To learn more about the implicit design pattern in Scala, please check out our gentle introduction to implicit classes and implicit conversions. We recommend reading these two in the provided order.
In the broader context of software design patterns, this is a simplified way of achieving the Decorator Design Pattern. We can “pimp” both core Scala libraries as well as third party libraries installed through sbt.
We usually “pimp” libraries to increase the expressiveness of our code.
3. Typical Use Case
Imagine that in a Slack workspace, we want new members to mention specific things about themselves while introducing themselves. We are interested in first name, primary language, and the number of years of industry experience.
So, introductions will strictly go like this: My name is X my primary programming language is Y I have Z years of industry experience.
Now, we have a Slack bot that matches such strings and can successfully pick up introductions for some arbitrary analysis. Maybe it can then recommend channels that may interest these members.
Our bot needs to know who is junior (0-3 years), mid-level (4-6 years), and senior (7+ years), as well as the name and programming language.
Let’s create a class to hold introduction information:
case class IntroText(text: String) {
val tokens = text.split(" ")
def name: String = tokens(3)
def level: String = tokens(12).toInt match {
case 0 | 1 | 2 | 3 => "Junior"
case 4 | 5 | 6 => "MidLevel"
case _ => "Senior"
}
def language: String = tokens(9)
}
We can test these to ascertain behavior:
"Bot" should "detect new member details" in {
val intro = IntroText(
"My name is Fauz my primary programming language is
Scala I have 7 years of industry experience"
)
assertResult(expected = "Senior")(actual = intro.level)
assertResult(expected = "Scala")(actual = intro.language)
assertResult(expected = "Fauz")(actual = intro.name)
}
4. Pimping String Class
The previous example is good, but we can do better. We can call the methods level, language, and name as though we were calling them directly on the String class. We can do this by pimping or decorating the String class using implicit conversion:
implicit def stringToIntroText(str: String): IntroText = IntroText(str)
The above definition is a normal Scala String -> IntroText method. It takes a String and converts it into an IntroText type. We’ve marked it implicit so that the Scala compiler can locate it without any further input from us.
Now IntroText does not need to appear in our usage:
"Pimped Bot" should "detect new member details" in {
val intro =
"My name is Bengi my primary programming language is Java I have 2 years of industry experience"
assertResult(expected = "Junior")(actual = intro.level)
assertResult(expected = "Java")(actual = intro.language)
assertResult(expected = "Bengi")(actual = intro.name)
}
A couple of things are happening under-the-hood. The Scala compiler encounters a method call such as level in a String instance, but such a method does not exist in the String class. So, it looks for any classes within the scope that have the given method, and it finds IntroText.
Then it goes ahead to check if there’s an implicit conversion from String to IntroText and upgrades the variable, intro, to an instance of IntroText.
5. Conclusion
In this article, we’ve explored the Pimp My Library Pattern, which is quite commonly used in Scala. It’s one way of applying the decorator pattern to make our code more expressive using existing Scala features.
As usual, the full source code is available over on GitHub.