If trait A extends B, then mixing in A gives you precisely B plus whatever A adds or extends. In contrast, if trait A has a self reference which is explicitly typed as B, then the ultimate parent class must also mix in B or a descendant type of B (and mix it in first, which is important).
That's the most important difference. In the first case, the precise type of B is crystallised at the point A extends it. In the second, the designer of the parent class gets to decide which version of B is used, at the point where the parent class is composed.
Another difference is where A and B provides methods of the same name. Where A extends B, A's method overrides B's. Where A is mixed in after B, A's method simply wins.
The typed self reference gives you much more freedom; the coupling between A and B is loose.
Since you're not clear about the benefit of these differences...
If you use direct inheritance, then you create trait A which is B+A. You have set the relationship in stone.
If you use a typed self reference, then anybody who wants to use your trait A in class C could
- Mix B and then A into C.
- Mix a subtype of B and then A into C.
- Mix A into C, where C is a subclass of B.
And this is not the limit of their options, given the way Scala allows you to instantiate a trait directly with a code block as its constructor.
As for the difference between A's method winning, because A is mixed in last, compared to A extending B, consider this...
Where you mix in a sequence of traits, whenever method
foo() is invoked, the compiler goes to the last trait mixed in to look for
foo(), then (if not found), it traverses the sequence to the left until it finds a trait which implements
foo() and uses that. A also has the option to call
super.foo(), which also traverses the sequence to the left till it finds an implementation, and so on.
So if A has a typed self reference to B and the writer of A knows that B implements
foo(), A can call
super.foo() knowing that if nothing else provides
foo(), B will. However, the creator of class C has the option to drop any other trait in which implements
foo(), and A will get that instead.
Again, this is much more powerful and less limiting than A extending B and directly calling B's version of