I'm going to repost something I had said in response to this article on another site. I think it's an awesome example of what can happen when you create a language with extensibility in mind.<p>-------------------<p>Scala solves this problem really well with typeclasses.<p>You can define a typeclass like<p><pre><code> case class Refrigerated[T](thing: T) // this is just something holding things that have been refrigerated.
trait Refrigeratable[T] { // this is the typeclass
def refrigerate(thing: T): Refrigerated[T]
}
</code></pre>
Now we have a refrigerated object that holds some object that's been refrigerated, and it looks like I'm about to do some class composition, but I'm not. Bear with me.<p>Now, we have some sort of data class<p><pre><code> case class Pillow(fluffiness: Int, warmness: Int)
</code></pre>
Pretty straight forward, we now have a Pillow with measurable fluffiness and warmth<p>Next, lets implement refrigerated for pillow.<p><pre><code> implicit val pillowRefrigerable: Refrigerable[Pillow] = new Refrigerable[Pillow] {
def refrigerate(pillow: Pillow): Refrigerated[T] =
Refrigerated(pillow.copy(warmness = pillow.warmness / 2))
}
</code></pre>
So there's an implementation of Refrigerable for pillow. Refrigerating a pillow just cuts the warmness in half.<p>So actually using the Refrigerable stuff. There's two ways to go about this. One way is to just use the Refrigerable instance to refrigerate the pillow.<p><pre><code> def refrigeratePillow(pillow: Pillow): Refrigerated[Pillow] = pillowRefrigerable.refrigerate(pillow)
</code></pre>
Nothing special there. You could basically do that in any language. So how does scala do it better? Well, we can define a refrigerate method for anything Refrigerable like this:<p><pre><code> def refrigerate[T](thing: T)(implicit refrigerableInstance: Refrigerable[T]): Refrigerated[T] =
refrigerableInstance.refrigerate(thing)
</code></pre>
Now this is really cool. Anything type that we have a Refrigerable instance for, can be used as an argument for refrigerate. And we don't have to explicitly state what Refrigerable instance we're using. Calling it looks like<p><pre><code> val refrigeratedThing = refrigerate(anythingWithARefrigerableInstanceHere)
</code></pre>
Okay. We still haven't gained a whole lot. All we've done so far, is make it so we don't have to explicitly state what Refrigerable to use. So let's go an extra step. Let's make it so we just call refrigerate on the Object itself rather than passing the object as an argument to a method. This is possible, because scala let's us make extension methods for generics.<p><pre><code> implicit class RefrigerableOps[T](thing: T)(implicit refrigerableInstance: Refrigerable[T]) {
def refrigerate: Refrigerated[T] = refrigerableInstance.refrigerate(thing)
}
</code></pre>
Okay, now this is really cool. We've defined a method for any type for which we have Refrigerable in scope. This means we can now refrigerate our pillow more easily:<p><pre><code> val refrigeratedPillow = myPillow.refrigerate
</code></pre>
What?! That's great! And it gets better. Now that we've implemented this boilerplate, giving other things this wonderful syntax is easy. Let's say I want to refrigerate my cousin, because she's too hot.<p><pre><code> case class Cousin(hotness: Int)
implicit val refrigerableCousin: Refrigerable[Cousin] = new Refrigerable[Cousin] {
def refrigerate(cousin: Cousin) =
Refrigerated(cousin.copy(hotness = cousin.hotness - 1))
}
</code></pre>
Now we can make our cousin less hot! Great!<p><pre><code> val refrigeratedCousin = cousin.refrigerate
</code></pre>
This is basically a good way to make things open to extension without subclassing it's awesome!<p>You can even do more to simplify making the Refrigerable instances. You can effectively make them one liners.