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".
I thought we were talking about "adding methods" to collection-like things so that they look as if they were built-in. And this was what the article was about: That it gets complex (and impossible) if you want to solve it for the general case.
It 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.
Fully agree. I have not trieded this but I think the typesystem features dylan has should work too (limit and unions).
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.
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:
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".