Implicit additions to types for DSL's (Scala)

One of the goals when designing an embedded/internal domain-specific-language (DSL) is extend the syntax of the host language in order to make operations and syntax more clear within a particular domain. However, most host languages don't provide a way for you to extend the operations or methods available to built-in types. This often leads to the creation of new types that provide both the same functionality as the built-in type and the extended functionality needed within a domain-specific context. In Scala, adding functionality to built-in types is easy. It can be achieved by harnessing the power of Scala's implicit conversions. This is made possible because of a couple of reasons. First, everything in Scala is an object. Second, there is no real difference between operations and methods. Third, when an operation or method doesn't exist for a given object's type, Scala checks to see if that object can be implicitly converted to a type that does provide that method.

Here's an example of how to add an "elemOf" operator to all types in Scala. Such an operator, by definition, would tester whether an element is an element of a Set. In order to do this, we need to create a wrapper class that defines the "elemOf" operator.

Note, we haven't done anything special. If we were to stop here, we'd have to explicitly create an instance of DSLRichAny anytime we wanted to use the "elemOf" operator. The magic happens when we tell Scala to implicitly create a new instance of DSLRichAny. Let's do this by creating an object for our DSL in which we define such an implicit conversion.

Now, any object that extends our DSL object will know that there is way to implicitly convert any object to an instance of DSLRichAny! Here's an example.

When the application is compiled, Scala sees that the "elemOf" operator/method is being called on a type that doesn't define it. It therefore checks to see if there is an implicit conversion defined, in scope, that would allow "elemOf" to be called. Since we're extending our DSL object, such a conversion is in scope and the compiler does the rest.