self => abuse, or, the baclava antipattern

Reading time ~7 minutes

Like many beginning Scala programmers, I was exposed to the Cake Pattern early on and told that this is how you do dependency injection in Scala. Coming from the Ruby world I thought it looked like an awfully heavy-weight method, but of course I didn’t know any other way yet. Right away I was placed on a project in which the Cake pattern was apparently very much in use, a CMS built on Play.

I was tasked with adding a sitemap feature, such that when the path /sitemap.xml was requested, a sitemap of the site would be rendered. This seemed straightforward enough. I would just need to pull some data about the site’s currently published pages from the database and massage it into some pretty straightforward XML. This being Play, I started with a controller, and right away knew I’d need to pull in whatever code pulls pages from the database, which was pretty easy to find. I soon found I would also want to pull in a trait for looking at the contents of the HTTP request. Again, no big deal.

trait SitemapController extends Controller
    with SiteRequestExtractorComponent
    with PageRepositoryComponent {

  def sitemap = {
    // the magic happens...
    Ok(sitemapXML)
  }
}

Simple enough, until I tried to compile:

[error] /Users/chuckhoffman/dev/cms/app/controllers/SitemapController.scala:48: illegal inheritance;
[error]  self-type controllers.SitemapController.type does not conform to models.page.CmsPageModule's selftype models.page.CmsPageModule with models.auth.UserRepositoryComponent with models.auth.GroupRepositoryComponent with models.approval.VersionApprovalComponent with models.email.EmailServiceComponent
[error]   with CmsPageModule

Hm. Looks like somebody used that Cake pattern thingy to inject dependencies into CmsPageModule having to do with users, user “groups,” and approval of new content. That probably has to do with who can do what kind of updating of pages, so even though that isn’t relevant to what I’m after since I only want to read page data, not update it, it still seems reasonable. I’ll just find the right traits that satisfy those three things – even though I’m not really using them here – and add withs for them and all should be good.

One little snag, I guess… it turns out that those traits were “abstract”, which meant greping through the code to find the correct “implementations,” which turned out to be UserRepositoryComponentPostgres, GroupRepositoryComponentPostgres, and MongoVersionApprovalComponent. (This is a common sort of thing to do, since one often wants to mock out the database for tests.) Took a while to track them down, but eventually I did. So surely I should be able to just add those three withs to the SitemapController, add the imports of them to the top of the file, and now we’re off and running, yeah?

[error] /Users/chuckhoffman/dev/cms/app/controllers/SitemapController.scala:48: illegal inheritance;
[error]  self-type controllers.SitemapController.type does not conform to models.page.CmsPageModule's selftype models.page.CmsPageModule with models.auth.UserRepositoryComponent with models.auth.GroupRepositoryComponent with models.approval.VersionApprovalComponent with models.email.EmailServiceComponent
[error]   with CmsPageModule
[error]        ^
[error] /Users/chuckhoffman/dev/cms/app/controllers/SitemapController.scala:51: illegal inheritance;
[error]  self-type controllers.SitemapController.type does not conform to models.approval.MongoVersionApprovalComponent's selftype models.approval.MongoVersionApprovalComponent with models.page.PageModule with models.treasury.TreasuryModule with models.auth.UserRepositoryComponent with models.auth.GroupRepositoryComponent with models.email.EmailServiceComponent with com.banno.utils.TimeProviderComponent
[error]   with MongoVersionApprovalComponent
[error]        ^

Oh. Looks like there’s now some kind of dependency here being enforced between pages and something having to do with email; also, versions, in addition to depending on pages, users, groups, and that same email thing again, also depend on… treasuries? Huh?

Plainly there’s a design problem here because I’m now being forced to mixin traits having to do with treasuries (these are bank websites) into a controller that makes a sitemap. At this point, however, I don’t know Scala well enough to pull off the refactoring this needs with all these self-types in the way. So off I go to find more traits to mixin to satisfy those self-types. Then those traits turn out to have self-types forcing mixin of even more traits, and so on.

After a day and a half of work, I finally had a working SitemapController.scala file containing about ten lines of actual “pulling web pages data from the database” and “building some XML,” and a couple dozen lines of mostly irrelevant withs and imports just so the bastard would compile.

It’s Time We Had A Talk About What A “Dependency” is

Consider this: given two modules (in the general sense of “bunch of code that travels together”, so Scala traits and objects, class instances, Ruby modules, and so forth, all apply) A and B, having, let’s say, a dozen functions each, if one of the functions in A calls one of the functions in B, does that make B a dependency of A?

I’ll save you the suspense. No, it does not. Or at least, not that fact alone. In fact, laying aside the concern that a dozen functions might be too many for one module anyway, it’s clear that the dependency is between those two functions, not the whole modules they happen to be in. Which suggests that that one function in that module is responsible for some functionality that may not be all that relevant to what the other eleven are for. In other words, you have a case of poor cohesion.

To the extent that we promote the Cake pattern to new Scala programmers before they have a handle on what good design in Scala looks like, I believe we’re putting the cart before the horse. The cake pattern, or more generally, cake pattern-inspired self-typing, takes your bad design and enlists the compiler to help cram it down others’ throats. Couple this with the fact that a lot of new Scala programmers think that: (1) because I’m writing Scala, I’m doing functional programming; (2) functional programming is the wave of the future and OO is on its way out, therefore (3) The past couple decades of software design thinking, coming as it does from the old OO world, has no relevance to me; and we get situations like my humble little sitemap feature.

Cake-patterned code, especially badly cake-patterned code (which has been the majority of cake-patterned code I’ve seen, which isn’t surprising given the pattern’s complexity – literally nobody I’ve talked to seems to quite completely “get” it, myself included), is needlessly difficult to refactor, not just because of the high number of different modules and “components” involved and/or because you have to very carefully pick apart all the self-types (especially when those have even more withs in them), but also because you frequently find yourself wanting to move some function A, but need to make sure it can still call some function B, but B turns out to be very difficult to find, let alone move – it might be in some trait extended by the module A is in, or it might be in some trait extended by one of those, or some trait extended by one of those, and so on, to the point where B could literally be almost anywhere in your project or any library it uses, and likewise anywhere in there could easily be completely different functions with the same name. All this just so that you can get the compiler to beat the next developer that has to maintain this code over the head with errors if he doesn’t extend certain traits in certain places, despite the fact that the compiler is already perfectly good at knowing if you’re trying to call a function that isn’t in scope.

To make matters worse, most folks’ introduction to functional programming these days still consists of pretty basic Lisp or Haskell use throwing all your program’s functions in one global namespace with no real modularization. It’s no surprise then if they see either the cake pattern or trait inheritance in general as simply a way of cramming more stuff into one namespace. Old Rails hands will hear echoes of concerns or more generally, the Ruby antipattern of “refactoring” by shoving a bunch of seemingly-sorta-related stuff out of the way into a module (it makes your files shorter on average, but doesn’t necessarily improve your design any).

Cohesion and coupling, separation of concerns, connascence, even things like DCI, these things still matter in Scala and in any of today’s rising functional or mostly-functional programming languages – or for that matter, any programming language that gives you the ability to stick related things together, which is pretty much all the useful ones. (I posit that DCI may be especially relevant to the Scala world as it seems like it would play nicely with anemic models based on case classes.)

I hate to keep harping on my Ruby past, but I heartily recommend Sandi Metz’s book Practical Object-Oriented Design in Ruby. Scala is really just kind of like a verbose, statically-typed Ruby plus pattern matching, when you think about it. Both combine OO and functional concepts, both have “single-inheritance plus mixins” multiple-inheritance; heck, even implicit conversions are just a way better way of doing what refinements are trying to do.

Ultimately though, the cake pattern has the same problem as used to be pointed to about those other “patterns” when they were all the rage: people learned the patterns early on, and started using them everywhere because they thought that was how you’re supposed to program now. They ended up with overly convoluted designs because they were wedging patterns in where they weren’t necessary or didn’t make sense, rather than first understanding the reasons the patterns existed, reaching for the patterns only when they found themselves facing the design puzzles the patterns are intended for.

Ending the indentation argument

{% img /images/line-break-after.jpg 400 original dank memeage %}I'm just going to come out and say it. I hate parameter aligning and I th...… Continue reading

I like to Blog and live my life. Thank you.

Published on December 20, 2014