For a real example, try designing generic data-structures in Java, that exposes the below functionality in a type-safe way:
scala> val list = List(9, 7, 5, 10, 11)
list: List[Int] = List(9, 7, 5, 10, 11)
scala> list.sum
res0: Int = 42
scala> list.sorted
res1: List[Int] = List(5, 7, 9, 10, 11)
scala> val stringList = List("a", "b", "c")
stringList: List[java.lang.String] = List(a, b, c)
scala> stringList.sum
<console>:9: error: could not find implicit value for
parameter num: Numeric[java.lang.String]
stringList.sum
^
scala> val bitSet = collection.immutable.BitSet(1,2,3,4)
bitSet: scala.collection.immutable.BitSet = BitSet(1, 2, 3, 4)
scala> bitSet.map(_ + 1) // the result is still a BitSet
res3: scala.collection.immutable.BitSet = BitSet(2, 3, 4, 5)
scala> bitSet.map(_.toString) // the result is not a BitSet
res4: scala.collection.immutable.Set[java.lang.String] = Set(1, 2, 3, 4)
scala> map.map(kv => (kv._1 + 1, kv._2 + 1)) // result is still a Map
res7: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 4 -> 5)
scala> map.map(kv => kv._1) // result will not be a map
res8: scala.collection.immutable.Iterable[Int] = List(1, 3)
In case you're wondering, you can't.Ironically, Martin Odersy, creator of Scala, is one of the core people behind Java Generics [0]
EDIT: Just to clarify, I'm a huge Scala fan, and understand Odersky had constraints and compromises to do on Java, which I think is probably one of the reasons he went on to create Scala.
var list = new List<int>() { 9, 7, 5, 10, 11 };
list.Sum();
list.OrderBy(i => i); // list.Sort() is in-place, so using functional OrderBy instead
var stringList = new List<string>() { "a", "b", "c" };
stringList.Sum(); // C# signals a typing error
var bitSet = new HashSet<int>() { 1, 2, 3, 4 }; // not sure what a Scala BitSet is, but it looks a collection of ints?
bitSet.Select(b => b + 1);
bitSet.Select(b => b.ToString());
// map wasn't defined in the snippet. Assuming it's like a Dictionary<int, int>?
var map = new Dictionary<int, int>() { { 1, 2 }, { 3, 4 } };
map.ToDictionary(kv => kv.Key + 1, kv => kv.Value + 1);
map.Select(kv => kv.Key);http://blogs.msdn.com/b/dotnet/archive/2013/09/12/immutable-...
C# is mutable-first but well capable of providing immutability (requires good developers but so does Scala/F# :p).
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.
I do prefer Scala's collection types and for comprehensions over LINQ though. I find them much easier to read and more powerful.