One of the best features of Ceylon is lexically-scoped introduction, which we discussed here, calling it decoration.
I've recently come to the conclusion that the explicit decorate statement is a barrier to modularity, and decided upon a slightly different approach. In this approach, an interface may declare that it adapts another type:
doc "Adaptor that introduces List to Sequence." see (List,Sequence) shared interface SequenceList<T> adapts Sequence<T> satisfies List<T> { shared actual default List<T> sortedElements() { //define the operation of List in //terms of operations on Sequence return asList(sortSequence(this)); } ... }
Then the interface is called an introduction - or, alternatively, an adapter, in a nod to the terminology used in the Go4 book. (In that book, a decorator
is a slightly different concept.) According to the spec:
The interface may not:
- declare or inherit a member that refines a member of any adapted type, or
- declare or inherit a formal or non-default actual member unless the member is inherited from an adapted type.
Now, to enable the introduction in a certain compilation unit, all you need to do is import it.
import ceylon.collection { SequenceList } //import the adapter String[] cities = { "Melbourne", "Atlanta", "San Francisco", "Guanajuato", "Paris" }; List<String> sortedCities = cities.sortedElements(); //call the adapter
Again, according to the spec:
If, in a certain compilation unit, multiple introductions of a certain adapted type declare or inherit a member that refines a common member of a common supertype then either:At runtime, an operation (method invocation, member class instantiation, or attribute evaluation) upon any type that is a subtype of all the adapted types is dispatched according to the following rule:
- there must be a unique member from the set of members, called the most refined member, that refines all the other members, or
- the adapted type must declare or inherit a member that refines all the members.
- If the runtime type of the instance of the adapted type declares or inherits a member defining the operation, the operation is dispatched to the runtime type of the instance.
- Otherwise, the operation is dispatched to the introduction that has the most-refined member defining the operation.
I think this is a significantly better approach, making introduction much easier to use.
UPDATE: A commenter asks if Ceylon will support C#-style extension methods. No need. Every concrete method of an adapter is an extension method! Indeed, an adapter without a satisfies clause is just
a package of extension methods and attributes.