Cake pattern concept reviewed

180 views
Skip to first unread message

Matan Safriel

unread,
Apr 9, 2015, 9:36:25 PM4/9/15
to scala...@googlegroups.com
Hi,

I had it on my list to review/learn the cake pattern (the etymology of which rather mysterious).
It seems like a very simple concept, supported by self type annotation. Perhaps you comment on the validity of this simple and abstract description as a replacement for the tedious long write-ups out there.

As I see it, the pattern just boils down to the simple and trivial notion that composite objects would adhere to interfaces (typically abstract traits, in scala) rather than hardwire the components, which they rely on. This means that functionality is mixed-in from concrete extensions of these traits. The mixing-in is done at the time of instantiating a composite object - therefore the composite object itself - is (in a sense) oblivious to which conforming implementations it got. 

The latter aspect also has the consequence of easy mocking for dependencies deemed outside the focus of a unit test (hence a replacement for crufty DI libraries).

Overall it is simply a more convenient / testable / flexible way of compositionality, that is safeguarded, in a typical scala implementation, by the self type constraints.

If you made it through the abstract description, thanks for your comment.

Cheers,
Matan


Jon Pretty

unread,
Apr 10, 2015, 2:02:50 AM4/10/15
to Matan Safriel, scala...@googlegroups.com
Hi Matan,

I'm responsible for the name. The intention (back in about 2006) was to illustrate both "slicing" (i.e. splitting into traits) and "layering". I think your description is good, but only covers the slicing aspect. The original intention behind layering was that cake offers the possibility of nesting inner traits within (and implicitly depending on) your outer traits, then composing them using self-types in parallel with the outer traits. You can nest inner traits arbitrarily deeply, though there are good reasons not to.

I'm not sure if the layering aspect is so widely considered part of the cake pattern these days, and some people have advocated the term "pancake" to describe a cake without layers, or "wedding cake" for a deeply nested cake.

Cake works because it allows dependencies across self-types to be very easily enforced with little or no specification of what those dependencies are, and often, the specification of the dependencies and their implementation are the same thing. This is the big  downside of cake: it becomes a bit too easy to create unintended dependencies between traits, simply because the surface area of all the non-private members of a trait used as a self type become its "public" API, and unless you're careful, it's too easy to add to that public API. Unravelling a cake pattern can be very painful.

Nevertheless, it remains a very convenient way of splitting a class across multiple files.

Cheers,
Jon



--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Roland Kuhn

unread,
Apr 10, 2015, 11:08:49 AM4/10/15
to Jon Pretty, Matan Safriel, scala-user
Hi Jon & Matan,

another use of the cake-pattern is to actually split up a class into multiple parts that have some privacy from each other in order to disentangle things that would otherwise be entangled by default. I have used this successfully to make the akka.actor.ActorCell manageable—it had grown to 2000 lines and breaking it up into multiple objects is not desirable because of object overhead and memory locality. It sure is a rather flat pancake, but I like pancakes :-)

Regards,

Roland


Dr. Roland Kuhn
Akka Tech Lead
Typesafe – Reactive apps on the JVM.
twitter: @rolandkuhn


Vlad Patryshev

unread,
Apr 10, 2015, 12:28:47 PM4/10/15
to Matan Safriel, scala-user
Matan,

In addition to Jon and Roland's explanations, here's how I practically use it.

I've a class that does a tedious job with a web site:
- figure out the list of patients
- get current payment data
- scan through all claims and process them.

The "before cake" solution would be to either have all this in one class, or introduce delegates doing this or that.
The cake solution is to create specific traits for patients extraction, for payment data extractions, for claims processing, like
trait ClaimsHandler { self: ProcessorOfEverything =>
  /* claim-related code */
}
and then write
class ProcessorOfEverything extends Something with PatientsExtractor with PaymentsExtractor with ClaimsHandler { /* almost nothing here */}

This could be refactored further down into cleaner components, if necessary.

The side effect is also that testability is easily available.

Thanks,
-Vlad

Matan Safriel

unread,
Apr 10, 2015, 1:05:31 PM4/10/15
to Vlad Patryshev, scala-user
Thanks Vlad,

It seems like caking this scenario only exchanges function "delegates" for cake-style traits, other than making testability much better. Would you see this entirely differently?

Thanks,
Matan

Jon Pretty

unread,
Apr 12, 2015, 11:02:11 AM4/12/15
to Matan Safriel, Vlad Patryshev, scala-user
Hi Matan,

Yes, it's not easy to find examples of the cake pattern in libraries. The Scala compiler itself is probably the best-known (and first) example, whereby a compiler instance can be configured by mixing in a set of traits such that things like reporting output and compile settings must all be provided for a concrete compiler to be instantiated. Different tools (e.g. the REPL, `scalac` and SBT) all instantiate variants of the cake.

Although I've used the cake pattern quite extensively in my own end-user applications, if I remember correctly, none of my open-source libraries use it, and I've found it to be difficult to configure cleanly. One use case I had a few years ago involved representing forms (e.g. web forms) as a cake, with fields as inner classes. I can't remember the details, but my implementation separated the submission and validation logic from the presentation layer into different cake slices, so that (for example) different front-ends could be used. The idea was that the presentation slices crosscut both the form itself, and the inner field classes.

One problem with this relates to the lack of virtual classes in Scala. For example, given a structure like this:

abstract class Form {
    type Field <: BasicField
      class BasicField
}

trait WebFrontEnd { this: Form =>
  type Field <: WebField
    trait WebField { this: Field => }
}

val form = new Form with WebFrontEnd

we have no way of instantiating a new instance of abstract type `Field` from inside `Form`, even though this is probably a necessity to do anything useful. We can work around this with some boilerplate, like so:

abstract class Form {
    type Field <: BasicField
      class BasicField
        def makeField(): Field
}

trait WebFrontEnd { this: Form =>
  type Field <: WebField
    trait WebField { this: Field => }
}

val form = new Form with WebFrontEnd {
    type Field = BasicField with WebField
      def makeField() = new BasicField with WebField
}

but that's quite a departure from the simplicity of using mixins at the call site as a simple way of describing how the form should work, e.g. `new Form with WebFrontEnd`, and having the compiler check that everything just nicely wires together.

I believe that standard whitebox macros could help here, though I've not attempted it yet, and there's a reasonable chance it needs something more powerful like annotation macros... maybe one day I'll find the time to give it a go. :)

Cheers,
Jon

--
Jon Pretty | @propensive

Matan Safriel

unread,
Apr 13, 2015, 3:33:11 PM4/13/15
to scala-user
Thank you for all the responses!
Reply all
Reply to author
Forward
0 new messages