Because you hit the cryptic too soon, and you can't really avoid it. And I generalize this to any such language that has this characteristic, not just Scala. You can't use C++ for very long without having to know huge swathes of it if you want to use the libraries created by others, and you want to be able to debug why they don't work perfectly. (Bearing in mind "working perfectly" also includes you having a correct mental model of how they work, which is tricky if you only have a subset of the language in your head!) Same for Haskell. Compare with Python, where you basically can learn a reduced subset of the language, yet still use libraries fairly effectively. You need to know the basics of having objects and calling methods. You don't need to know how to write your own iterator, any of the double-underscore methods or the resulting protocols, you probably don't need to know decorators (and even if you do, probably just how to use them, because they are unlikely to bite you in odd ways if you just copy-paste instances of them), you don't need to know metaclasses, etc.
Type systems of almost any kind are very hard to satisfy without understanding. In some sense, this is one of the very constraints the stronger ones are trying to enforce.
I don't give a dime about higher-kinded types, type bounds, type views, type constraints, CanBuildFrom ... and I'm happily using the language.
I guess it would take me the same amount of time fully understand this article. Other people in this thread have shown how easy it is in other languages too.
Dylan (and other multi-dispatch languages) don't have the problem of "adding methods to objects/classes", because methods are standalone entities (first-class, whereas in Scala the are not) that exist independently of the data they operate on.
so you can just "add" a method to something by defining it:
define method upcase(s :: String) => (ret :: String)
// code here
end method upcase;
"abc".upcase // => "ABC"
// is just sugar for
upcase("abc") // => "ABC"
so you could define 'forward-iteration-protocol (a method that returns 8 values) on anything in Dylan (built-in or not) to make it a "Sequence".In fact, the thing you described is trivial in Scala.
implicit def Upcase(s: String) = new { def upcase = s.toUpperCase }
"abc".upcaseIt wasn't abut type-safety, just about complexity.
The problems in Scala arise, because you have to extort yourself if you want to "add a method" in the privileged position after the dot. You have to to resort to implicit conversions which are a non-composible feature. To fake composibility the author has to wade through huge piles of complexity.
These problems don't arise in Dylan at all, because there is no privileged argument position (the receiver) and you can just define methods for anything without conversions to wrappers or monkey-patching. Namespacing is done via the module system and lexical scope.
A collection-like thing in Dylan is any object where someone has implemented the required methods (first and foremost: forward-iteration-protocol). All those methods can be implemented without having access to the definition of the objects class or type, so things like native arrays (with only .length indexing and value setting as operations, akin to Java arrays) can be made collection-like.
Then you can just define your own methods for collections like filter-map, which will then work for thoose native Arrays.
If you only use the minimal collection protocol:
define method filter-map(coll, pred :: <function>, transform :: <function>)
let <ret-type> = type-for-copy(coll); // analogous to CanBuildFrom
let new-coll :: <ret-type> = make(<ret-type>); // analogous to Builder
let (init, limit, next, end?, key, elt) =
forward-iteration-protocol(coll);
for (state = init then next(coll, state),
until: end?(coll, state, limit))
let e = elt(coll, state);
if(pred(e))
add!(new-coll, e));
end if;
end for;
new-coll;
end method upcase;
If map and choose (filter) are already defined (And yes; they are in terms of the collection protocol): define method filter-map(coll, pred :: <function>, transform :: <function>)
map(transform, choose(pred, coll));
end method filter-map;
Yes, I don't really like the API-design of forward-iteration-protocol. It works like iterators in Java, but is designed to not need allocation for simple indexable collections like lists and vectors etc.The most importend methods to expand are element (getting the n's object), forward-iteration-protocol and add. For a immutable collection that all you really need.
The real complexity is when you add method M to collection C and expect that class A which does not have any relationship with C also gets the method.
It has been shown that it is possible in Scala, without all the unnecessary complexity shown in the authors post.
Still, I fail to see any statically typed language even coming close to what is requested from Scala.