Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Why is it so hard to keep your data structures immutable in C# or Java? C# (my primary language) has parallel collections as of .Net 4.0 and with .Net 4.5 you have the async keyword support on top of the TPL. Could you elaborate on the differences between Scala and C# which make Sala more preferable for you? I'm not trying to start a war on C# vs Scala, I'm genuinely interested in the differences (I enjoy learning new languages, but for the right reasons)


It's hard because of the type-system. Experiment with Scala's immutable data-structures and try implementing them in Java. For example, lack of support for type-classes, not to mention the really shitty support for generics in Java, designed probably by sado-masochists, makes that really painful to do and there's a world of difference between Guava and Scala's library.

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.


> not to mention the really shitty support for generics in Java, designed probably by sado-masochists

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.

[0] - http://en.wikipedia.org/wiki/Martin_Odersky


Ironical yes, but the reason generics is hampered in Java is backward-compatiblity, which was not Martin's decision from what I read.


The parent commenter was mostly asking about C# to Scala, so for comparison, here's you snippet in C# with LINQ. It's a little more verbose -- C# generics need to be a little more explicit -- but I think you'd agree it's pretty close. It's equally typesafe, but it is not immutable.

            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);


And of course it can be made immutable using this:

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).


Thanks @curveship for replying.

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.

[1] http://en.wikipedia.org/wiki/Type_class

[2] http://en.wikipedia.org/wiki/Expression_problem


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.


Looks like your bitSet is a HashSet. Not good.


I can't talk too much about C#, but in Java there is no first class support for `map`, 'filter`, `reduce` etc. (with Google's Guava, you can imitate these things, but not so elegantly). So transforming a data structure involves a for-loop and lots of mutable operations. Iterating and transforming a collection in a functional style is much nicer.


I agree with you on the functional style. C#'s LINQ solves this problem quite well, although they named them differently (map -> select, filter -> where, reduce -> Aggregate). I've seen a number of people laud C# for being functional-ish because of such features.


LINQ is a fantastic DSL akin to Scala's collection types. Odersky gives C# the nod as one of the few other traditionally imperative languages to have such a rich way of transforming data in his Functional Programming with Scala coursera course.

I do prefer Scala's collection types and for comprehensions over LINQ though. I find them much easier to read and more powerful.


You are explaining it a bit wrong. C# provides delegates (Action, Func etc.), lambda expressions, expression trees, anonymous functions, anonymous types, extensions methods. Those features make it a functional language and make succinct monads possible. LINQ happens to be one of those monads, but you can implement yours (e.g. rename Select to map etc.).


Yes I agree with you. I'm not as well versed in monads and the technicalities around them as I would like to be, so thank you for pointing that out!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: