5 ms·
Stackable Traits Pattern in Scala
- lmm 11y agoI like the way Scala solves the problem with multiple inheritance as described in https://fuhm.net/super-harmful/ https://fuhm.net/super-harmful/ : multiple inheritance is allowed, but only classes (not traits) may have constructors, and a class may only have one parent class (i.e. only one parent with a constructor). For my part I still prefer composition over inheritance and prefer to do composition "by hand" (i.e. explicit members and delegation) where necessary; I wouldn't use this style at all. But Scala's great strength (and great weakness) is that it has absolutely first-class support for traditional OO like this as well as all the functional stuff.
- jackweirdy 11y agoCould you expand on why you would say having support for traditional OO is a great weakness?
- lmm 11y agoIt means there's a diversity of Scala styles; one person's Scala won't necessarily look like someone else's. More concretely it causes a lot of problems for type inference (particularly when ADTs in Scala are modeled as traits with implementing classes, so e.g. the type of Some(x) ends up as Some rather than Option). Other ML family languages that don't have traditional OO inheritance generally have perfect type inference (at least in the absence of higher kinds) via Hindley-Milner.
- mafribe 11y agoPractical type inference for Hindley-Milner-style languages with higher kinds is not a problem, see e.g. [1]. The problem is adding sub-typing. I'm also not 100% sure it's correct to say that ML-style languages have no problem with type-inference. I'm sure Robin Milner would have preferred having full, unrestricted parametric polymorphism (i.e. System F) in ML. Alas System F has undecidable type inference [2]. Hindley-Milner is a restriction of full parametric polymorphism that enjoys type inference, and is expressive enough to type many programs. BC Pierce says that the Hindley-Milner "has hit a sweetspot". I concur. [1] M. P. Jones, A system of constructor classes: overloading and implicit higher-order polymorphism. http://www.cs.tufts.edu/comp/150GIT/archive/mark-jones/fpca93.pdf http://www.cs.tufts.edu/comp/150GIT/archive/mark-jones/fpca9... [2] J. B. Wells, Typability and type checking in System F are equivalent and undecidable. http://www.macs.hw.ac.uk/~jbw/papers/f-undecidable-APAL.ps.gz http://www.macs.hw.ac.uk/~jbw/papers/f-undecidable-APAL.ps.g...
- oselhn 11y agoI think that it does not make sense that Jamie extends Tywin etc. This is not "is a" relation.
- jinst8gmi 11y agoI think it's more that Joffrey etc. inherits certain traits from his parents rather that is his parents.
- acjohnson55 11y agoThat's a good point. It's one of the things that confused me about trait stacking when I first started learning Scala. People often effectively use traits to implement composition, in the sense that the intention isn't that you would take advantage of Liskov substitution, but rather you intend to aggregate behavior from a number of sources. It's one of many ways in Scala where a single abstract construct could have several different semantics applied to it.
- akavi 11y agoYeah, the author is being cute with the word "inheritance", which ends up obscuring the actual point.
- saheb37 11y agoI didn't intend to screw up the actual point, maybe I did but I think the actual point was still understandable, isn't it? If not please let me know the actual point where the article lost it in your case.
- saheb37 11y agotraits in Scala doesn't always have to be in a "is a" relationship. If it makes you understand then think of them as adding behavior/mixins. And about why the syntax confused you, the first "with" trait has to be extended, if it could replace extends with "with" in case of traits then maybe you would have liked it better.
- 11y ago
- brandonmenc 11y ago> The example is based on characters of Game of Thrones, to make understanding concepts less boring. For those of us who don't know anything about Game of Thrones, and can't pronounce Elvish names, the example in this article reads like: Flarg → Twipptch → Glorp → Boop → Fwyxnth → AnyRef → Any So instead of being less boring, I have to use a decoder ring to figure out what's going on here.
- krschultz 11y agoAgreed. Disappointing because I think it's well written beyond that.
- saheb37 11y agoWill try to improve in my next post :)
- saheb37 11y agoI agree to your point, but was it that difficult to decode or follow till the end? You think A, B, C, D would have been better? Or something more logical names to everyone
- brandonmenc 11y agoNo, it wasn't impossible to follow, but the extra layer of indirection made me jump back to the top a few times to make sure I had everything straight. //Legacy abstract class Lannister //Father trait Tywin extends Lannister could have just been: abstract class Family trait Father extends Family Otherwise, I appreciate the article - it was informative.
- saheb37 11y agoThanks, got your point, will keep it more simple and generic next time :)
- jbooth 11y agoAlternate view: https://en.wikipedia.org/wiki/Composition_over_inheritance https://en.wikipedia.org/wiki/Composition_over_inheritance
- twic 11y agoThis sort of is composition. It's like a static version of the Decorator pattern. Here's a vaguely realistic example: http://blog.ryannedolan.info/2014/03/scala-pattern-stackable-traits.html http://blog.ryannedolan.info/2014/03/scala-pattern-stackable...
- jbooth 11y agoNot really, at all. Order the traits differently and you can get different behavior. Inherit one subtrait vs another and you can get different behavior. You can reason about all of this, but I'm not that smart, so I prefer composition. Composition locks away the behavior you want so that you know exactly what you're getting.
- twic 11y agoI don't get it. If you're decorating, then ordering the decorators differently gets you different behaviour. It's exactly the same. Indeed, you could write a straightforward mechanical method to convert any trait stacking into a decoration.
- jbooth 11y agoYeah -- I'm arguing for composition because it's harder to screw up, at the cost of being less pretty. The manner and order in which composed objects are delegated to is crystal clear if you're wiring them yourself (or with your IDE). I've seen some messy, confusing code involving traits and just slapping 'with Thingy' 10 times in a row. The phrase 'emergent behavior' is how I would best describe it.
- joefkelley 11y agoThis seems like more of an antipattern to me. If you have to be thinking about linearization order, your hierarchy is probably too complex.
- SolarNet 11y agoI think the point is that if the hierarchy is complex enough that you have to think about it it's simple enough to figure. The cases presented are obviously over complex to describe the behavior.
- saheb37 11y agoMaybe to make it more interesting, it turned out to complex as you are saying. But idea was not to use A, B, C etc. Will try to come up with something different next time.
- saheb37 11y agoI have explained how linearization works, if you just wanna use it, its very easy. Just goes from right to left and you don't need to remember anything else. About anti pattern, I don't think so. It might not be useful every where, but at some places it makes sense to have stackable behavior.