C# is more capable and saner than Java and to tell you the truth, C# has been the first static language that I used with joy. If it weren't for Scala, I would have prefered C#. Skipping over platform limitations and considering just the language, I still prefer Scala any day of the week.
Let me outline the differences between the examples I've posted and the ones that you did.
list.sum
In Scala this example outlines the usage of the Numeric[T] type-class. A type-class [1] is like an implemented interface in Java and C#, except that you don't need the ability to modify the type in order to implement this interface. See the Expression Problem [2].
In C# or Java, the concept of adding numbers is not being exposed as a standard interface. In C# the + operator is a static member, so it cannot be part of an interface (in Scala there is no such thing as static members btw). In Java the + operator is something special reserved for primitives. In Scala it doesn't matter how + is exposed, because you can implement a Numeric[T] for whatever type you want and hence you can treat any type you want as a number in generic data-structures and algorithms. If you implement your own Complex type or RomanNumeral type, no problem, as you can sum them up as long as you also implement Numeric[Complex] and Numeric[RomanNumeral].
In contrast, for your sample to work, the C# engineers had to add extension method overloads of IEnumerable.Sum() for each number in the system. But what do you do if you want to add your own number implementation, like a BigDecimal or something with better performance for your use case. Yes, in Scala it works, without hacks.
list.sorted
In Scala, this uses the Ordering[T] type-class. Again, it's an open interface to which you can add any type, even types whose implementations you don't control. Also, the C# version is NOT statically type-safe, while the Scala version is. This compiles just fine, but fails at runtime ...
var list = new List<object>() { 1, "2", 3 };
list.Sort()
Want to guess why that is? Because in C# you also can't add extra method usage restrictions on the generic types of a class. So you can't have a "sorted" method on a class that only works in case your initialized T is part of the Ordering type class. I.e. you either have to restrict T to some interface site-wide (so you can't use your class for anything but things that can be sorted), or you have to rely on runtime reflection / runtime errors.
There is value in doing this, rather than doing it with an OrderBy that takes a function for the comparison operation. First of all, many types have natural ordering and the inability to implement this means that you can't express natural ordering in your language. Also, these are really simple examples. You don't see the value of this until you start working with these tools in the real world in more and more complex problems.
Your examples with the HashSet and the Dictionary are wrong. They aren't doing the same thing.
In Scala, a BitSet is a Set of integers that stores those integers efficiently in-memory. It's a different implementation from a regular HashSet, specialized on storing integers. When mapping over a BitSet, you want the result to still be a BitSet, otherwise you lose its properties. But the result can't be a BitSet if you're not returning integers in that mapping function. You missed the point of my example.
So basically Scala allows you to override the returned type, based on the given mapping function and it does so in a statically type-safe way, as the real return type is noticed by the compiler (i.e. it's compile-time ad-hoc polymorphism, not runtime). And best of all, the mechanism is pluggable so you can always override the given defaults.
Of course, you can have dozens of utility methods that allow you to escape the limitations of your language. You can build anything on top of Java and C# actually.
It's still death by a thousand cuts and in case you can't see it, ask yourself why you haven't done functional programming in C#, even though it is possible to do so. And yes I know, some people do it, just like some people are doing OOP in C.
No, thank you for replying. I'm not sure I deserve it -- a little while after I posted, I realized that my C# in many cases missed the point, and in a couple was flat out wrong (BitSet). That led me down a path of researching more about Scala ... and after some neat reading, my "gotta get back to real work" timer dinged, and I never got back here to clarify. So thank you for the insightful and detailed reply. My post's main value was in eliciting yours :).
Thank you very much for this explination. I need some time to go through the details and subtleties of what you are saying, but it appears to be a great example of some of the shortcomings of C# compared to Scala.
C# is more capable and saner than Java and to tell you the truth, C# has been the first static language that I used with joy. If it weren't for Scala, I would have prefered C#. Skipping over platform limitations and considering just the language, I still prefer Scala any day of the week.
Let me outline the differences between the examples I've posted and the ones that you did.
In Scala this example outlines the usage of the Numeric[T] type-class. A type-class [1] is like an implemented interface in Java and C#, except that you don't need the ability to modify the type in order to implement this interface. See the Expression Problem [2].In C# or Java, the concept of adding numbers is not being exposed as a standard interface. In C# the + operator is a static member, so it cannot be part of an interface (in Scala there is no such thing as static members btw). In Java the + operator is something special reserved for primitives. In Scala it doesn't matter how + is exposed, because you can implement a Numeric[T] for whatever type you want and hence you can treat any type you want as a number in generic data-structures and algorithms. If you implement your own Complex type or RomanNumeral type, no problem, as you can sum them up as long as you also implement Numeric[Complex] and Numeric[RomanNumeral].
In contrast, for your sample to work, the C# engineers had to add extension method overloads of IEnumerable.Sum() for each number in the system. But what do you do if you want to add your own number implementation, like a BigDecimal or something with better performance for your use case. Yes, in Scala it works, without hacks.
In Scala, this uses the Ordering[T] type-class. Again, it's an open interface to which you can add any type, even types whose implementations you don't control. Also, the C# version is NOT statically type-safe, while the Scala version is. This compiles just fine, but fails at runtime ... Want to guess why that is? Because in C# you also can't add extra method usage restrictions on the generic types of a class. So you can't have a "sorted" method on a class that only works in case your initialized T is part of the Ordering type class. I.e. you either have to restrict T to some interface site-wide (so you can't use your class for anything but things that can be sorted), or you have to rely on runtime reflection / runtime errors.There is value in doing this, rather than doing it with an OrderBy that takes a function for the comparison operation. First of all, many types have natural ordering and the inability to implement this means that you can't express natural ordering in your language. Also, these are really simple examples. You don't see the value of this until you start working with these tools in the real world in more and more complex problems.
Your examples with the HashSet and the Dictionary are wrong. They aren't doing the same thing.
In Scala, a BitSet is a Set of integers that stores those integers efficiently in-memory. It's a different implementation from a regular HashSet, specialized on storing integers. When mapping over a BitSet, you want the result to still be a BitSet, otherwise you lose its properties. But the result can't be a BitSet if you're not returning integers in that mapping function. You missed the point of my example.
So basically Scala allows you to override the returned type, based on the given mapping function and it does so in a statically type-safe way, as the real return type is noticed by the compiler (i.e. it's compile-time ad-hoc polymorphism, not runtime). And best of all, the mechanism is pluggable so you can always override the given defaults.
Of course, you can have dozens of utility methods that allow you to escape the limitations of your language. You can build anything on top of Java and C# actually.
It's still death by a thousand cuts and in case you can't see it, ask yourself why you haven't done functional programming in C#, even though it is possible to do so. And yes I know, some people do it, just like some people are doing OOP in C.
[1] http://en.wikipedia.org/wiki/Type_class
[2] http://en.wikipedia.org/wiki/Expression_problem