Questions about Cake patterns

34 views
Skip to first unread message

Nicholas Ren

unread,
May 22, 2013, 9:18:11 PM5/22/13
to scala...@googlegroups.com
Hi everyone,

I just read and enjoyed the Cake pattern article. but I have few questions about this approach.

1. what's the syntax meaning of self-type annotation? 
e.g. this: UserRepositoryComponent => 
is it declaring a  field in the component trait? what's the meaning of '=>'?

2. do we really have to create a "component trait" for each service or repository? what's the advantage of cake pattern?


-- 
Nicholas Ren 

Andrew Conway

unread,
May 22, 2013, 9:42:54 PM5/22/13
to scala...@googlegroups.com
Question (1) is I think easy to explain - it means that the (abstract) type being created is an instance of some other type. This means that to make a non-abstract instance of it, the non-abstract instance must also be an instance of the other type. (effectively enforcing dependency injection).

Question (2) is harder.
 Part (1) : If you don't create the traits, you don't get most of the benefit of the modularization that dependency injection is for.
 Part (2) : I will leave this to someone who gets the cake pattern better than me. I don't understand how a self type is more powerful than abstract vals/types (apart from effectively allowing multiple abstractions in one one description).

Ben Hutchison

unread,
May 22, 2013, 10:18:46 PM5/22/13
to scala...@googlegroups.com
Hi Nicholas,

re 1: basically, that means that the current trait must have a
UserRepositoryComponent mixed into it, to be instantiated. In other
words, the this pointer (self) must have type UserRepositoryComponent,
hence the name 'self type'.

So you can assume to any fields/methods that UserRepositoryComponent
defines will exist on whatever concrete subclass ends up being
created.

This one of the distinguishing "innovations" of Scala's language and
type-system. There's a fairly famous paper, "Scalable Component
Abstractions", describing it.
lampwww.epfl.ch/~odersky/papers/ScalableComponent.pdf

That paper talks about some of the benefits of Cake (although Im not
personally 100% convinced), and Daniel Spiewak did a good talk at
NEScala:

http://nescala.org/#keynote

-Ben
> --
> You received this message because you are subscribed to the Google Groups
> "Melbourne Scala User Group" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to scala-melb+...@googlegroups.com.
> To post to this group, send an email to scala...@googlegroups.com.
> Visit this group at http://groups.google.com/group/scala-melb?hl=en-GB.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

King Lung Chiu

unread,
May 23, 2013, 12:19:54 AM5/23/13
to scala...@googlegroups.com
Hi Ben,

On 23 May 2013 12:18, Ben Hutchison <brhut...@gmail.com> wrote:
> Hi Nicholas,
>
> re 1: basically, that means that the current trait must have a
> UserRepositoryComponent mixed into it, to be instantiated. In other
> words, the this pointer (self) must have type UserRepositoryComponent,
> hence the name 'self type'.

This reminds me of a question I've had for a while:

Since this self-typing effectively mandates that the concrete type
must extend / implement UserRepositoryComponent in order to use the
trait, how is it different to having the trait directly extend
UserRepositoryComponent (without implementing
UserRepositoryComponent's abstract methods)?

ie. how's self-typing in MyTrait different to doing this instead?

trait MyTrait extends UserRepositoryComponent { ... }

class MyClass extends UserRepositoryComponent with MyTrait { ... }


cheers,

King

Ben Hutchison

unread,
May 23, 2013, 12:44:17 AM5/23/13
to scala...@googlegroups.com
On Thu, May 23, 2013 at 2:19 PM, King Lung Chiu <kinglu...@gmail.com> wrote:
> ie. how's self-typing in MyTrait different to doing this instead?
>
> trait MyTrait extends UserRepositoryComponent { ... }
>
> class MyClass extends UserRepositoryComponent with MyTrait { ... }

Hi King,

One difference is that is allows mutual dependence between any number
of traits, whereas inheritance forces the dependencies to be acyclic &
directed.

trait X { self: Y, Z => ...}
trait Y { self: X, Z => ...}
trait Z { self: X, Y => ...}

The idea, as Ive heard it, is that you can break your functionality
into interdependent components X, Y, Z, without committing to a
concrete implementation, then weld them together at the end to make an
app:

trait XImpl extends X {..}
trait YImpl extends Y {..}
trait ZImpl extends Z {..}

class MyApplication extends XImpl with YImpl with ZImpl

..Da dah! ..but given that traits dont accept constructor args, I
guess you need a small army of lazy vals in MyApplication to pass X, Y
and Z their inputs/config.

-Ben

Ken Scambler

unread,
May 23, 2013, 12:46:55 AM5/23/13
to scala...@googlegroups.com
Bring on trait constructors, whenever they figure out how to do it.    It should be mentioned that it is generally a bad idea to use abstract vals in a trait (as opposed to just def).


Andrew Conway

unread,
May 23, 2013, 12:48:09 AM5/23/13
to scala...@googlegroups.com
Ah! Thanks Ben. I had wondered about that myself too.

Tony Morris

unread,
May 23, 2013, 12:52:13 AM5/23/13
to scala...@googlegroups.com
On 23/05/13 14:46, Ken Scambler wrote:
> It should be mentioned that it is generally a bad idea to use abstract
> vals in a trait (as opposed to just def)
This is not true.

Without abstract val, many useful constructs become impossible. In
particular, smart constructors and therefore, data hiding. This fallacy
is so prolific that some have gone to the effort of attempting to debunk
it in blogs and so on, but that hasn't been effective.

What is generally a bad idea is (de)referencing an abstract val during
instance initialisation. There is no general rule that can be written here.

--
Tony Morris
http://tmorris.net/

Ken Scambler

unread,
May 23, 2013, 1:44:17 AM5/23/13
to scala...@googlegroups.com
Well the initialisation order thing "generally" comes up a lot when people use abstract vals, whereas the useful cases (where you need a fixed val as part of a type path or import) come up less frequently, at least in code I've seen.


Tony Morris

unread,
May 23, 2013, 1:55:05 AM5/23/13
to scala...@googlegroups.com
Sure that is a bias. I see way more people using abstract val appropriately than not.

However, in this case, formulating a rule leads to not only incorrect information, but discouragement from writing neat code. In other words, the statement, "do not use abstract val in trait" implies "do not always write neat code." It is also not possible to say, "do not use abstract val in trait, except ..." or, "Use abstract val in trait only if ..."

It is better to abandon the rule altogether, not just because it isn't true.

Ken Scambler

unread,
May 23, 2013, 2:17:38 AM5/23/13
to scala...@googlegroups.com
Fair enough; that's why I didn't say "never" or "do not".  Also, I normally use runtime checks for my smart constructors, so I probably don't see the useful cases as much as you do.

Tony Morris

unread,
May 23, 2013, 4:14:56 AM5/23/13
to scala...@googlegroups.com
The point is that this effort toward a statement or guideline is (at least) arbitrary and so apologising along the path of its formulation (e.g. with handwavy adverbs) leads you into a hole, even when qualified with those apologies (emphasise: EVEN THEN). This technique, even taking into account apologetics in any form, leaves you with nothing or a falsehood. Personally, I'd prefer nothing i.e. no guideline.

But the practical consequences don't stop there… adding to the falsehood (if the effort is not abandoned), this statement simply says, by implication, "write unsafe (or messy) code" and nothing else. This situation is worse than no guideline. It is not only useless, but proactively encouraging undesirable consequences.

These practical consequences are what compels me and at least one other guy (there is a blog post somewhere, but I failed to find it) to stamp out this fallacy. That is to say, it is not because the statement is false that compels rebuttal, but that the consequences of belief are quite terrible! This topic was also "discussed" and resolved on twitter a few weeks ago (though, Twitter is terrible for discussion).

Smart constructors are necessary for reasons beyond safety checks. A good example is the zipper in argonaut. Consider how to implement without the smart constructor. An abstract val in a trait is necessary for implementing this. The alternatives are unsafe code. This is undesirable in the strongest sense.

Go ahead, formulate a rule. Make it first true, then useful. I propose starting here: "Dereference an abstract val at instance initialisation causes … examples of this are …"

Finally, I contend, we should discontinue making false statements, especially those that are harmful, even if accidentally.

Ben Hutchison

unread,
May 23, 2013, 7:06:39 AM5/23/13
to scala...@googlegroups.com

Tony Morris

unread,
May 23, 2013, 7:08:12 AM5/23/13
to scala...@googlegroups.com
Haha, it's just a bad idea that needs to stop existing!

Lachlan O'Dea

unread,
May 23, 2013, 8:29:55 AM5/23/13
to scala...@googlegroups.com
On Thursday, May 23, 2013 6:14:56 PM UTC+10, Tony Morris wrote:

[...] 

These practical consequences are what compels me and at least one other guy (there is a blog post somewhere, but I failed to find it) to stamp out this fallacy. That is to say, it is not because the statement is false that compels rebuttal, but that the consequences of belief are quite terrible! This topic was also "discussed" and resolved on twitter a few weeks ago (though, Twitter is terrible for discussion).

I thought this blog post was good: http://blog.rintcius.nl/post/scala-traits-as-well-defined-modules-and-a-crime-scene-investigation.html. I'm not sure if that's the one you meant.

Lachlan.

Ken Scambler

unread,
May 23, 2013, 7:59:47 PM5/23/13
to scala...@googlegroups.com
This is a badge of honour! :)

I'll look further into Argonaut.

Tony Morris

unread,
May 23, 2013, 8:46:33 PM5/23/13
to scala...@googlegroups.com
I have been moved by Ken Scambler!
Reply all
Reply to author
Forward
0 new messages