6 ms·
One problem with extension methods is that you cannot statically express the idea that multiple classes implement the same extension methods. In particular, you
by catnaroek 13y ago
One problem with extension methods is that you cannot statically express the idea that multiple classes implement the same extension methods. In particular, you cannot make a generic whose type parameter is guaranteed to implement a given set of extension methods. This, in my opinion, reduces the usefulness of extension methods from "potential game changer" to "mere syntactic sugar".
- bad_user 13y agoYou mean type-classes? Scala and Haskel have them. It's certainly nice to be able to do statically type-checked stuff like: list.sorted list.max list.sum The problems with languages like C# (or Java) go beyond the lack of type-classes or similar. For example, given a type such as List<T>, you're restricted to define methods only "for all T", as these languages don't allow you to define methods only "for some T". Examples include methods such as "sorted" or "max", relying on type T implement some sort of Ordering interface, or "sum", relying on type T to be some sort of Number. So even if you would have a way to specify that types X, Y and Z have implementations for this and that set of extension methods, it would still be of somewhat limited use, given that one wouldn't be able to also use this in code making heavy use of generics, much like how interfaces themselves aren't used much in code making use of generics, because code making use of generics is usually some dumb Container<T>, where almost nothing about T is known. The best part about type classes is that you don't have to control/modify the type's implementation, in order to make it implement a certain type-class. Type-classes are like open interfaces. If the builders of C# never bothered to provide you with some common interface you want for built-in types, that's OK, because you can make one up. Scala also has implicit conversions and "view bounds". Implicit conversions go beyond extension methods and View Bounds allow one to specify that some type X can be viewed as some type Y (either Y inherits from X, or there exists an implicit conversion from X to Y). Something like: def sample[X <% Y](item: X) = item.callMethodFromY But these are mostly useful for builtins and deprecated for general-purpose usage in favor of context bounds (type-classes). It's still nice that builtins such as Array[T] is viewable as a Seq[T] and Strings are viewable (in true FP tradition) as sequences of chars, allowing one to do this: "some string".map(_.toUpper) // => "SOME STRING" What's interesting about the above example is that mapping over a string, produces a string, highlighting another Scala feature that goes beyond type-classes: "some string".map(_.toString) // => Seq(s, o, m, e, " ", s, t, r, i, n, g) This is the CanBuildFrom pattern. See this awesome example from M. Odersky himself: http://stackoverflow.com/a/1728140 http://stackoverflow.com/a/1728140
- catnaroek 13y ago> You mean type-classes? Yep. > Scala and Haskel have them. Correction: Haskell has them, Rust has a limited form of them, and Scala encodes them using a horrible hack. > The problems with languages like C# (or Java) go beyond the lack of type-classes or similar. For example, given a type such as List<T>, you're restricted to define methods only "for all T", as these languages don't allow you to define methods only "for some T". Examples include methods such as "sorted", relying on type T to be part of the Ordering type-class, or "max", relying on type T to be part of the Number type-class. Both C# and Java have bounded generics. The problem is that bounded quantification is fundamentally tied to class/interface hierarchies. > The best part about type classes is that you don't have to control/modify the type's implementation, in order to make it implement a certain type-class. Type-classes are like open interfaces. I know. > Scala also has implicit conversions and "view bounds". To be honest, I am not fond of those. (And, in general, I am not fond of the way Scala does things.) If I want to view an X as a Y, a regular function "f : X -> Y" can already do that. The notion of subtyping roughly corresponds to the idea that certain functions of the form "upcast : Subtype -> Supertype" are worth implicitly applying. I do not agree with this idea.
- bad_user 13y ago> Haskell has them, and Scala encodes them using a horrible hack You can view it like that, my perspective is that in Scala the underlying mechanism (passing the dictionary around by means of implicit parameters) is more explicit. Much like how prototypes in Javascript work, versus class-based OOP. With prototypes you can have class-based design, but you can also go beyond that. And this is similar to how Scala deals with type-classes. This does have a cost in terms of learning curve, but then you've got extra flexibility. CanBuildFrom is not a type-class. On implicit conversions and view bounds, I'm not fond of them either. I actually hope that they'll go away completely. I think they made it in the language (and survive), only because Scala runs on the JVM and they had to make the builtins (like Arrays, Strings) behave nicely without much overhead. In Haskell, Strings really are lists of chars. In Scala you can't do it, unless they introduced their own String class, which would have been awful, or if they introduced some sort of implicit conversion mechanism, which they did. And this could have been some magic that Scala's compiler applies for builtins, but personally I dislike compiler magic (that you can't hook into) more than I dislike hacks made for interoperability (if only they got rid of that implicit conversion from Any to String in Predef, which is so freaking annoying).