As far as I can see, no declaration clashes would occur if mutable.IndexedSeqLike extended mutable.SeqLike. Is the lack of inheritance an artifact of incremental design, or an actual decision?
Probably the former, at this change when Vector became IndexedSeq.
SeqLike was created subsequently to patch an oversight.
It happens that IndexedSeqLike has fewer abstract methods than SeqLike because iteration is defined in terms of indexing. So there's no motivation for extending it.
The SeqLike.transform method was added with a bunch of unrelated changes, so that wouldn't necessarily drive unifying the interfaces.
The *Like traits are implementation templates, merely. collection.IndexedSeqLike does inherit behavior from collection.SeqLike.
One reason not to extend a trait unnecessarily is that it could change linearization order, even if all actual concrete implementations do mix in the traits.
Related
I have read that with Scala, it is generally advised to use Traits instead of Abstract classes to extend a base class.
Is the following a good design pattern and layout? Is this how Traits were intended to replace Abstract?
client class (with def function1)
trait1 class (overrides function1)
trait2 class (overrides function1)
specificClient1 extends client with trait1
specificClient2 extends client with trait2
I don't know what your source is for the claim that you should prefer traits over abstract classes in Scala, but there are several reasons not to:
Traits complicate Java compatibility. If you have a trait with a companion object, calling methods on the companion object from Java requires bizarre MyType$.MODULE$.myMethod syntax. This isn't the case for abstract classes with companion objects, which are implemented on the JVM as a single class with static and instance methods. Implementing a Scala trait with concrete methods in Java is even more unpleasant.
Adding a method with an implementation to a trait breaks binary compatibility in a way that adding concrete methods to a class doesn't.
Traits result in more bytecode and some additional overhead related to the use of forwarder methods.
Traits are more powerful, which is bad—in general you want to use the least powerful abstraction that gets the job done. If you don't need the kind of multiple inheritance they support (and very often you don't), it's better not to have access to it.
The last reason is by far the most important in my view. At least a couple of the other issues might get fixed in future versions of Scala, but it will remain the case that defaulting to classes will constrain your programs in ways that are (at least arguably) consistent with good design. If you decide you actually really do want the power provided by traits, they'll still be there, but that'll be a decision you make, not something you just slip into.
So no, in the absence of other information, I'd suggest using an abstract class (ideally a sealed one) and two concrete classes that provide implementations.
OTOH, traits allow you to build and test the functionality of complex objects in a granular fashion, and to reuse core logic so as to provide different flavors. For example, a domain object might be deployed to a data server, which persists to a database, while a web server might employ read-only versions of the same object that are updated from the data server.
Nothing is suitable for every scenario. Use the right construct for the task at hand. Sometimes the reality of an implementation brings to light issues for specific use cases which were unknown at design time. Re-implementing using different assumptions and constructs can yield surprising results.
If I add a new case class, does that mean I need to search through all of the pattern matching code and find out where the new class needs to be handled? I've been learning the language recently, and as I read about some of the arguments for and against pattern matching, I've been confused about where it should be used. See the following:
Pro:
Odersky1 and
Odersky2
Con:
Beust
The comments are pretty good in each case, too. So is pattern matching something to be excited about or something I should avoid using? Actually, I imagine the answer is "it depends on when you use it," but what are some positive use cases for it and what are some negative ones?
Jeff, I think you have the right intuition: it depends.
Object-oriented class hierarchies with virtual method dispatch are good when you have a relatively fixed set of methods that need to be implemented, but many potential subclasses that might inherit from the root of the hierarchy and implement those methods. In such a setup, it's relatively easy to add new subclasses (just implement all the methods), but relatively difficult to add new methods (you have to modify all the subclasses to make sure they properly implement the new method).
Data types with functionality based on pattern matching are good when you have a relatively fixed set of classes that belong to a data type, but many potential functions that operate on that data type. In such a setup, it's relatively easy to add new functionality for a data type (just pattern match on all its classes), but relatively difficult to add new classes that are part of the data type (you have to modify all the functions that match on the data type to make sure they properly support the new class).
The canonical example for the OO approach is GUI programming. GUI elements need to support very little functionality (drawing themselves on the screen is the bare minimum), but new GUI elements are added all the time (buttons, tables, charts, sliders, etc). The canonical example for the pattern matching approach is a compiler. Programming languages usually have a relatively fixed syntax, so the elements of the syntax tree will change rarely (if ever), but new operations on syntax trees are constantly being added (faster optimizations, more thorough type analysis, etc).
Fortunately, Scala lets you combine both approaches. Case classes can both be pattern matched and support virtual method dispatch. Regular classes support virtual method dispatch and can be pattern matched by defining an extractor in the corresponding companion object. It's up to the programmer to decide when each approach is appropriate, but I think both are useful.
While I respect Cedric, he's completely wrong on this issue. Scala's pattern matching can be fully-encapsulated from class changes when desired. While it is true that a change to a case class would require changing any corresponding pattern matching instances, this is only when using such classes in a naive fashion.
Scala's pattern matching always delegates to the deconstructor of a class's companion object. With a case class, this deconstructor is automatically generated (along with a factory method in the companion object), though it is still possible to override this auto-generated version. At all times, you can assert complete control over the pattern matching process, insulating any patterns from potential changes in the class itself. Thus, pattern matching is simply another way of accessing class data through the safe filter of encapsulation, just like any other method.
So, Dr. Odersky's opinion would be the one to trust here, particularly given the sheer volume of research he has performed in the area of object-oriented programming and design.
As for where it should be used, that is entirely according to taste. If it makes your code more concise and maintainable, use it! Otherwise, don't. For most object-oriented programs, pattern matching is unnecessary. However, once you begin to integrate more functional idioms (Option, List, etc) I think you'll find that pattern matching will significantly reduce syntactic overhead as well as improving the safety offered by the type system. In general, any time you want to extract data while simultaneously testing some condition (e.g. extracting a value from Some), pattern matching will likely be of use.
Pattern matching is definitely good if you are doing functional programming. In case of OO, there are some cases where it is good. In Cedric's example itself, it depends on how you view the print() method conceptually. Is it a behavior of each Term object? Or is it something outside it? I would say it is outside, and makes sense to do pattern matching. On the other hand if you have an Employee class with various subclasses, it is a poor design choice to do pattern matching on an attribute of it (say name) in the base class.
Also pattern matching offers an elegant way of unpacking members of a class.
After watching Martin's keynote on Reflection and Compilers I can't seem to get this crazy question out of my head. Martin talks among other things about the "(Wedding) Cake Pattern", where traits play the central part. I'm wondering, why in the world do we need packages when we already have traits? Is there anything a package can do, what a trait (at least theoretically) cannot?
I'm not talking about the current implementation, I'm just trying to imagine what programming would be like if we replace packages with traits. In my head it would be like this:
one keyword less (package is unneeded)
no need for package objects
To summarize all my questions:
Is it theoretically possible to remove packages from the language and use traits instead.
What other benefits would we gain from this change? (I was thinking about first class packages and first class imports, but mixin composition is a compile time thing, although the super calls are dynamically bound)
Is Java/JVM compatibility the only thing, which would stand in the way?
Update
Daniel Spiewak talks in this keynote about the Dependency Injection being just the top of the iceberg of all the stuff you can do with the Cake Pattern.
Martin Odersky has said that Scala could get by with just traits, objects, methods and paths (I hope I didn't forget something).
Both classes and packages are just there because Scala is intended to be a hosted language, i.e. a language which runs on (this is actually not the interesting bit) and interoperates with (this is the important point) a host platform. Some of the host platforms that Scala is intended to interoperate with are the Java platform and the CLI, both which have a concept of classes and packages (namespaces in the case of the CLI) that is significantly distinct enough that it cannot be easily expressed as traits or objects. This is unlike interfaces, which can be trivially mapped to and from purely abstract traits.
The above statement was made in a discussion about potentially removing generics from Scala, because everything generics can do can also be achieved by abstract types.
In scala the object and package serve almost the same purpose and objects are also called modules. Objects deserve to be thought of as modules because they can contain any definition including other objects of course and, significantly, types.
A trait can be thought of as an abstract module. It can contain any definition and any member can be abstract including, again significantly, type members. I am reciting all this just to highlight the symmetry. Perhaps OT but to me traits seem to be as big an innovation in scala as the merging of object and functional ideas.
To finally give an answer:
I think packages could be removed in favour of objects (not traits).
The benefit would be a simplification - package objects would not need to be explicitly defined.
I think packages are distinct from objects for Java/JVM compatibility.
Some more commentary: in the video Martin talks of traits (abstract modules) more than concrete modules because the latter only appear at the last moment to assemble and reify some combination of abstract modules.
It is good to use abstract modules even when not "mixing a cake". e.g. when sketching out some code you might define a module to contain definitions. But as soon as you come to a type or value you are not ready to fill in, don't supply a dummy such as null. Instead switch the object to a trait and leave the member abstract.
It seems like Traits could completely replace interfaces, abstract base classes, mixins, and multiple inheritance, leaving you with just Traits and concrete inheritance.
Is this the intent?
If you have traits, which of the other code structuring constructs should you use?
(Roles are the Perl name for Traits.)
At least for Perl's Moose, there are no interfaces, so roles clearly subsume those, and generally mixins too. I'd say there still could be a case for abstract base classes. Roles can be considered what objects do, where classes are what they are.
By this line of reasoning, there still might be a valid use for an abstract base class. A URL is one example. There could easily be an abstract base class for a URL. An IO stream might be different, perhaps better as a role, as it defines how things behave rather than what they are.
When using roles, however, I have yet to see any clear need for true multiple inheritance from more than one class.
I have no use for interfaces or abstract classes at this point, but mixins and multiple inheritance are really enabled by traits so the usage of those paradigms is strongly encouraged here. Check the entire collection library to see the very rich classes you can build using these ideas.
Ah, my comments reflect Scala - I didn't realize you tagged this with multiple languages.
When you instanciate a trait; it consumes one classe.
So regardless of expressivity; You may still use legacy construct for preventing classes explosion in your jar (and starting time).
I let others answer about expressivity :)
I'm only talking about Scala here...
Read this.
In java world (more precisely if you have no multiple inheritance/mixins) the rule of thumb is quite simple: "Favor object composition over class inheritance".
I'd like to know if/how it is changed if you also consider mixins, especially in scala?
Are mixins considered a way of multiple inheritance, or more class composition?
Is there also a "Favor object composition over class composition" (or the other way around) guideline?
I've seen quite some examples when people use (or abuse) mixins when object composition could also do the job and I'm not always sure which one is better. It seems to me that you can achieve quite similar things with them, but there are some differences also, some examples:
visibility - with mixins everything becomes part of the public api, which is not the case with composition.
verbosity - in most cases mixins are less verbose and a bit easier to use, but it's not always the case (e.g. if you also use self types in complex hierarchies)
I know the short answer is "It depends", but probably there are some typical situation when this or that is better.
Some examples of guidelines I could come up with so far (assuming I have two traits A and B and A wants to use some methods from B):
If you want to extend the API of A with the methods from B then mixins, otherwise composition. But it does not help if the class/instance that I'm creating is not part of a public API.
If you want to use some patterns that need mixins (e.g. Stackable Trait Pattern) then it's an easy decision.
If you have circular dependencies then mixins with self types can help. (I try to avoid this situation, but it's not always easy)
If you want some dynamic, runtime decisions how to do the composition then object composition.
In many cases mixins seem to be easier (and/or less verbose), but I'm quite sure they also have some pitfalls, like the "God class" and others described in two artima articles: part 1, part 2 (BTW it seems to me that most of the other problems are not relevant/not so serious for scala).
Do you have more hints like these?
A lot of the problems that people have with mix-ins can be averted in Scala if you only mix-in abstract traits into your class definitions, and then mix in the corresponding concrete traits at object instantiation time. For instance
trait Locking{
// abstract locking trait, many possible definitions
protected def lock(body: =>A):A
}
class MyService{
this:Locking =>
}
//For this time, we'll use a java.util.concurrent lock
val myService:MyService = new MyService with JDK15Locking
This construct has several things to recommend it. First, it prevents there from being an explosion of classes as different combinations of trait functionalities are needed. Second, it allows for easy testing, as one can create and mix-in "do-nothing" concrete traits, similar to mock objects. Finally, we've completely hidden the locking trait used, and even that locking is going on, from consumers of our service.
Since we've gotten past most of the claimed drawbacks of mix-ins, we're still left with a tradeoff
between mix-in and composition. For myself, I normally make the decision based on whether a hypothetical delegate object would be entirely encapsulated by the containing object, or whether it could potentially be shared and have a lifecycle of its own. Locking provides a good example of entirely encapsulated delegates. If your class uses a lock object to manage concurrent access to its internal state, that lock is entirely controlled by the containing object, and neither it nor its operations are advertised as part of the class's public interface. For entirely encapsulated functionality like this, I go with mix-ins. For something shared, like a datasource, use composition.
Other differences you haven't mentioned:
Trait classes do not have any independent existence:
(Programming Scala)
If you find that a particular trait is used most often as a parent of other classes, so that the child classes behave as the parent trait, then consider defining the trait as a class instead, to make this logical relationship more clear.
(We said behaves as, rather than is a, because the former is the more precise definition of inheritance, based on the Liskov Substitution Principle - see [Martin2003], for example.)
[Martin2003]: Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Prentice-Hall, 2003
mixins (trait) have no constructor parameters.
Hence the advice, still from Programming Scala:
Avoid concrete fields in traits that can’t be initialized to suitable default values.
Use abstract fields instead or convert the trait to a class with a constructor.
Of course, stateless traits don’t have any issues with initialization.
It’s a general principle of good object-oriented design that an instance should always be in a known valid state, starting from the moment the construction process finishes.
That last part, regarding the initial state of an object, has often helped decide between class (and class composition) and trait (and mixins) for a given concept.