tag:blogger.com,1999:blog-22587889.post4489064465464541598..comments2024-02-11T13:21:47.930+05:30Comments on Ruminations of a Programmer: Modular Abstractions in Scala with Cakes and Path Dependent TypesAnonymoushttp://www.blogger.com/profile/01613713587074301135noreply@blogger.comBlogger20125tag:blogger.com,1999:blog-22587889.post-63363760602844401092013-02-13T15:12:21.983+05:302013-02-13T15:12:21.983+05:30I agree and would go with your definition of the c...I agree and would go with your definition of the cake pattern, if only it didn't contradict with the non-cake examples that Jonas Boner gave in his article (and he seems to have coined the name, at least I haven't found an earlier reference).rintciushttps://www.blogger.com/profile/00452597567784473542noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-38320828268910517752013-02-11T08:55:30.243+05:302013-02-11T08:55:30.243+05:30@rintcius - Absolutely - I at least think so. If y...@rintcius - Absolutely - I at least think so. If you look at the traditional definition of a design pattern, it's never tied to a particular implementation technique. I can implement a strategy either through runtime polymorphism w/ subtyping or with generics and parameteric polymorphism. So I would like to see all of these as various implementation variations of the Cake pattern.Anonymoushttps://www.blogger.com/profile/01613713587074301135noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-25368656879404080722013-02-10T23:04:32.417+05:302013-02-10T23:04:32.417+05:30Hmm ... if I interpret your definition of cake pat...Hmm ... if I interpret your definition of cake pattern correctly, then it seems that what Jonas Boner described here (http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di/) as *alternatives* to the cake pattern (structural typing, implicit declarations & using Google Guice) would be part of your definition of the cake pattern.rintciushttps://www.blogger.com/profile/00452597567784473542noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-65931622967758978112013-02-06T21:40:39.383+05:302013-02-06T21:40:39.383+05:30@rintcius - Think functional .. DI as function cur...@rintcius - Think functional .. DI as function currying which I blogged at http://debasishg.blogspot.in/2010/02/dependency-injection-as-function.html and with Reader monad on which I talked at http://vimeo.com/23547652 ..Anonymoushttps://www.blogger.com/profile/01613713587074301135noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-90202013236217570152013-02-06T17:59:14.471+05:302013-02-06T17:59:14.471+05:30Re 1: Is there any form of DI that you would not l...Re 1: Is there any form of DI that you would not label as the cake pattern then?<br /><br />Re 2: Yeah indeed, there's a trade-off between keeping things DRY and keeping things abstract.rintciushttps://www.blogger.com/profile/00452597567784473542noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-14448271366515840932013-02-06T14:17:44.863+05:302013-02-06T14:17:44.863+05:30@rintcius -
1. I see the Cake pattern as an idiom...@rintcius - <br />1. I see the Cake pattern as an idiom where you build stuff separately and delay the mixing as late as possible. Self annotation is only one of the neat ways to do that. So I would like to view "self annotations" as <i>one of the</i> implementation vehicles for the pattern.<br /><br />2. Yes, I could do that. But the general philosophy of the Cake pattern is to delay the concretization of instances as long as possible. Maybe in this use case it doesn't seem natural to give bal another value than semantics.bal. But there may be other use cases where it makes sense. Consider a decorator that just aggregates various semantics and adds logic on top. In that case the decorator may have some different value. The bottomline is I usually follow the principle of keeping values and types abstract as long as I can.Anonymoushttps://www.blogger.com/profile/01613713587074301135noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-234002007417249802013-02-06T14:08:40.412+05:302013-02-06T14:08:40.412+05:30@Adam Warski - true. In case of self annotations a...@Adam Warski - true. In case of self annotations also you need to specify the annotation in both the clients. Admit having to declare the abstract val is a bit more of a typing. But the real value that the path dependent type based approach adds is that it allows you to be more consistent with types in the use case of a decorator as in Auditing implementation of the post. Auditing is an implementation of Portfolio and it also consumes another Portfolio. So effectively it has two BalanceComponents. But by doing val bal: semantics.bal.type (as in the code), we prevent the inconsistency. Auditing is bound to use the same variant of BalanceComponent (and hence the Balance type) of the Portfolio that it consumes. I really found this part quite illuminating in the design.Anonymoushttps://www.blogger.com/profile/01613713587074301135noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-47236646279357486632013-02-04T16:50:03.798+05:302013-02-04T16:50:03.798+05:30Nice write up, Debashish. Thanks for the food for ...Nice write up, Debashish. Thanks for the food for thought! I have a couple of questions:<br /><br />1) You must have another idea of the cake pattern than I have, since you say you are still applying the cake pattern. To me the basic parts of the cake pattern is the use of self-type annotations and its use of components. So in my world you would *not* apply the cake pattern here, since you are not using self-type annotations. I think I would call what you describe here "type injection" analogous to "value injection" which I described here (and I described it as moving away from the cake pattern): <br />http://blog.rintcius.nl/post/di-on-steroids-with-scala-value-injection-on-traits.html<br />As an attempt to get to a clearer set of definitions, I am curious what exactly the cake pattern is to you (cannot find an exact definition of it anywhere).<br /><br />2) Do you see any use-case to give bal another value than semantics.bal in extensions of Auditing? Or in other words, what about assigning <br />"val bal: semantics.bal.type = semantics.bal"<br />in Auditing instead of ClientPortfolioAuditService1 and ClientPortfolioAuditService2?rintciushttps://www.blogger.com/profile/00452597567784473542noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-16928662569193006542013-02-04T00:46:52.703+05:302013-02-04T00:46:52.703+05:30Unless I'm mistaken, if there were two clients...Unless I'm mistaken, if there were two clients which used the BalanceComponent, the concerete instance would have to be specified in both of them, as a val?<br /><br />This is quite similar to a simplistic class-with-constructor dependency injection.<br /><br />In a normal cake pattern, this only needs to be done once.Adam Warskihttps://www.blogger.com/profile/04535036247623614805noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-62306354098874500382013-02-01T21:16:52.974+05:302013-02-01T21:16:52.974+05:30@Debasish yes my bad sorry, I meant 'type Bala...@Debasish yes my bad sorry, I meant 'type Balance[T]' / 'type Balance[T] <: BalanceLike[T]'Alois Cochardhttps://www.blogger.com/profile/00906716915320183913noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-63860947195146184572013-02-01T21:06:01.368+05:302013-02-01T21:06:01.368+05:30@Alois - I am still a bit confused when you talk a...@Alois - I am still a bit confused when you talk about typeclass for Balance or BalanceLike. For typeclass we need to have a type constructor and here we r using Balance as an abstract type within BalanceComponent. But surely you can constrain the Balance type - I think the Precog blog post that Daniel wrote used that pattern. Anonymoushttps://www.blogger.com/profile/01613713587074301135noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-79480402335670661692013-02-01T21:01:50.615+05:302013-02-01T21:01:50.615+05:30@Unknown - True, not much different. However I lik...@Unknown - True, not much different. However I like how val bal: semantics.bal.type in Auditing enforces that the decorator and the decorated use the same representation of Balance type. Don't think that's possible without a path dependent type.Anonymoushttps://www.blogger.com/profile/01613713587074301135noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-64240126672549429652013-02-01T17:12:43.779+05:302013-02-01T17:12:43.779+05:30@Pascal: yes this work as long as BalanceAbsract i...@Pascal: yes this work as long as BalanceAbsract is *out* of the cake, what frustrated me with some cake libraries I used is that some types which doesn't use anything from the cake was part of it, making what you describe unfortunately not possible. one should be very careful about not putting everything in the cake without thikning about consequence imo.<br /><br />@Debasish: About my first concern, I confess my example was a bit weird because Balance is existential, but let say you want to contstrain it using something like "BalanceLike", then it could make sense to have a typeclass for BalanceLike... that's the kind of concrete issue I had.<br /><br />Regarding the second one, yes that's solve this issue, but it still quite verbose in my opinion. And in your example you have only 2 'consumers', but when you start to have a complex set of collaborators it might be tricky to define type for the one that need to be able to exchange data. Maybe it's not... I should probably experiment more.<br /><br />It sounds to me we are fixing a problem we introduced by trying to solve an other one, and we end doing this "Bakery of Doom", I have the feeling there should be a better way of doing that...<br />Alois Cochardhttps://www.blogger.com/profile/00906716915320183913noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-21411631608988888772013-02-01T16:55:39.282+05:302013-02-01T16:55:39.282+05:30Hi, interesting, that’s indeed the pattern that is...Hi, interesting, that’s indeed the pattern that is used in <a href="https://github.com/TiarkRompf/virtualization-lms-core/blob/develop/test-src/epfl/test1-arith/Arrays.scala#L32-L41" rel="nofollow">LMS</a>. But I don’t think it differs much from the use of selftype annotations.Unknownhttps://www.blogger.com/profile/06650004088183270059noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-2552540568237662242013-02-01T16:42:14.228+05:302013-02-01T16:42:14.228+05:30@Alois - Balance is a type abstracted within Balan...@Alois - Balance is a type abstracted within BalanceComponent. So by design I don't want the Balance type to escape the component. In this design I would like to have multiple implementations of BalanceComponent, which I can do very well as shown in the post. I think I am missing the rationale for a typeclass for Balance here.<br /><br />The idea that this design espouses is that we can have multiple BalanceComponent implementations that can be flexibly plugged into Portfolio implementations without the Portfolio getting to know the details of the underlying types of Balance and yet being able to access through path-dependent types. I guess this is what we also call existential types.<br /><br />Regarding your second concern, isn't the Auditing abstraction an example ? Auditing is an implementation of Portfolio and it also consumes another Portfolio. So effectively it has two BalanceComponents. But by doing val bal: semantics.bal.type (as in the code), we prevent the inconsistency. Auditing is bound to use the same variant of BalanceComponent (and hence the Balance type) of the Portfolio that it consumes. I really found this part quite illuminating in the design.Anonymoushttps://www.blogger.com/profile/01613713587074301135noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-27680500057513310922013-02-01T16:29:55.619+05:302013-02-01T16:29:55.619+05:30@Andreas Textor - Thanks for pointing out the typo...@Andreas Textor - Thanks for pointing out the typo. Now fixed. Will post my observations on Alois' comment soon ..Anonymoushttps://www.blogger.com/profile/01613713587074301135noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-50617426821492921752013-02-01T16:26:09.263+05:302013-02-01T16:26:09.263+05:30Thank you for the interesting article. Just a quic...Thank you for the interesting article. Just a quick note on a typo: In the 3rd code block, line 21, b._2 should probably be: b.currency.<br />Also looking forward to your reply on Alois' comment!Anonymoushttps://www.blogger.com/profile/00024434492404876576noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-5964166939287145962013-02-01T16:21:33.731+05:302013-02-01T16:21:33.731+05:30Interesting idea!
@alois : I agree about your con...Interesting idea!<br /><br />@alois : I agree about your concerns about path-dep types potential difficulties. Need to study further. Just about your 1st concern, would it be heretic to do the following?<br /><br /> trait BalanceAbstract<br /> case class BalanceCustom(amount: Double, cur: Currency, date: Date) extends BalanceAbstract<br /><br />and then constrain the Balance type:<br /> trait BalanceComponent {<br /> type Balance <: BalanceAbstract<br /> ...<br /> }<br /><br />Then you could build your typeclass on BalanceAbstract.<br /><br />(BTW I'm @mandubian on twitter ;))<br /><br />pascalhttps://www.blogger.com/profile/12090516153432374101noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-90519805332193051142013-02-01T15:39:47.352+05:302013-02-01T15:39:47.352+05:30That's an interesting way to avoid self annota...That's an interesting way to avoid self annotation, even if I don't consider them to be an issue as long they are not mutually recursive.<br /><br />But my main issue with cake pattern, comes from path-dependent type, I really like them when designing DSL, but I faced situation where using them for modularization went bad... I'll try to explain.<br /><br />My first concern, is that if I want to now create a typeclass for BalanceComponent.Balance, I cant' do it the same way I design usually type class.<br /><br />I mean the typeclass can't be declared at top level, it need to be 'in the cake', it's not a big deal but that give the feeling that something could be done in a better and more flexible way.<br /><br />More generally any abstraction you'll want on a type which is part of the cake, will have to be in the cake too, and I found that frustrating and make me generate a good amount of boilerplate.<br /><br />My second concern, is that you can end in situation where your abstract type become incompatible while they concrete definition are actually the same.<br /><br />Let's say I have three components which consume BalanceComponent (like Portoflio): A, B and C.<br />They each use they own BalanceComponent, but A consume B and C as well.<br /><br />Maybe I missed something in my design, but I've run to the issue where even if the concrete Balance type was the same, I wasn't able to exchange value between A/B/C because the path is different and it was not compiling. <br /><br />I confess this could be seen as somewhat complex use case, but this happened to me multiple time. As for now I prefer to stay away from cake as much as possible and prefer using implicit to share collaborators (and with a bit of https://github.com/aloiscochard/sindi to ease the pain).<br /><br />Unfortunatly as soon as I use a library which abuse of cake (Akka/Slick/...), I can't escape myself from entering the cake.<br /><br />Would love to know what is your opinion about these kinds of issues I've run into?Alois Cochardhttps://www.blogger.com/profile/00906716915320183913noreply@blogger.comtag:blogger.com,1999:blog-22587889.post-44159839553623701982013-02-01T12:40:52.003+05:302013-02-01T12:40:52.003+05:30That looks like a great approach. Im just slightly...That looks like a great approach. Im just slightly worried that most of the types in a programm would end up as path-dependent. The deeper the nesting, the longer the paths would become which might be kind of hard to understand.<br /><br />Anyway, I'll have to try it out. Thanks for sharing!asflierlhttps://www.blogger.com/profile/06454355877884089805noreply@blogger.com