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

A lot of the "cons" in the technical section just sound like a large engineering organization making pragmatic decisions. I can't think of any company working at that scale that's successfully integrated a non-trivial amount of functional or logic programming. As much as hackers might not like it, there are reasons that coding at that scale is almost always a matter of banging out a lot of imperative code in a boring language like Java.

And I'm definitely not the only one that appreciates Go precisely because it doesn't play the Scala game of integrating every single academic PL research feature of the last 20 years but instead tries to be as practical and simple as possible.



When I squint at Go's syntax, underneath the C-like facade I see:

* higher order, first class functions * lists (squint at the slice a bit; also chans) * maps * flexible structs (curiously, you attach "methods" to them much as you attach functions to Haskell typeclasses).

If anything, driven by pure pragmatism for the sake of concurrency and distributed computing, Go actually does implement really awesome functional primitives. However, it's built to be familiar to C and doesn't talk about it much.

Very, very clever.


Personally, I love the OP's response to your last comment:

"I hate Scala with a passion, for what it's worth. I just wish there had been more advocacy for it."

I wouldn't characterize myself as hating Scala, but I understand where he's coming from on both points. There are reasons that this static typing fan tends to prefer Clojure to Scala on the JVM. Also, it's just a beautiful quote.


From what I've heard, Go goes too far in the opposite direction and integrates basically no modern research.


Characterizing Go like that would be too harsh. It certainly does integrate more experience than research, which is why Go is almost as often explained in terms of what it doesn't do as for what it does.


Go is not a research language (e.g. Haskell), it is pragmatic. I suspect a part of the design decisions of Go factored in training for people using that language. At a certain point, features that are alien cost more in training costs than they will gain in "elegance" or whatever.


I don't think the concern is training, but the complexity caused by how various features interact, and how hard they make to reason about code.

Go is designed so it is very easy to read and reason about what code does.


I don't know. Maybe it's because I've been mostly looking at Haskell and OCaml recently, but most of the more "researchy" features and languages I've seen aim to be easier to reason about.

Unlike most languages (including Go, I believe), researchy languages tend to have very well defined semantics based on well-understood math--really going out of the way to be easy to reason about!


That's what I like the most about it. All the context you need to understand a block of code is right there, but it's still less verbose than something like Java.

We all acknowledge that we spend more time reading than writing code but for some reason we still design languages that make writing it the priority.


Have you witnessed any problems resulting from Scala "integrating every single academic PL research feature of the last 20 years"?

(I was under the impression that aside from the actor model of concurrency, Scala typically gets used just like Java but with cleaner code.)


As always: it depends.

As a peripheral observer and user of Scala, I feel like it gets a lot of things right if you want to use it as a "better Java". But that doesn't mean it's particularly usable as a "better Java"; obviously reasonable people can disagree but personally I find it uncomfortable to write Scala code for too long. It feels like it leads to awkward structures (blocks within blocks within blocks, hard-to-follow but oh-so-functional transforms) and an odd sort of terseness that reminds me of Python (not a compliment, I find Python irritating to read even though I like a lot of the ways it does stuff). It's like there's so much stuff there that it encourages you to be way too clever. "Too clever" is a thing and a bad one; somebody (maybe even you) has to puzzle out your cleverness later.

I do find cases where the language actually does cause problems, though. There appears to be three main groups involved with Scala: PL academics, "better Java" people, and "why isn't it Haskell?" people. The last group is, to me, rather frustrating, because it's the group that gets up in arms at the idea of a 'for' statement in the language. (The standard method of iteration is a foreach method over a collection and it is insanely slow; people resort to writing while loops because the foreach construct is so laughably slow. Why am I faking a for loop in such a "modern" language?) There are a few areas like this where practicality has apparently lost out to zealotry and it makes the language feel vaguely hostile to somebody who already Does The Right Thing in Java (immutable-by-default, dependency injection rather than creation, etc. etc.). As silly as it may sound, when I'm writing Scala I don't feel welcome in my own code--and I've been using Scala off and on for a couple of years, it's not just fear of the unknown.

I'm actually really looking forward to Kotlin (and Ceylon, to a lesser extent) because it's a language with a focus. JetBrains knows where they want to go with it instead of just dumping a box of stuff on your lawn and telling you to piece together what might or might not actually be what you want. The comparison always reminds me of The Big Lebowski: "Say what you want about the tenets of National Socialism, dude, but at least it's an ethos!"


> foreach is insanely slow

no, in 2.9 using a 1 to 10 generator and a foreach was slower than using one using primitives due to the boxing. This is fixed in trunk.

> gets up in arms at the idea of a 'for' statement in the language

Scala's for is an expression, you can use it as a statement by not yielding anything if you want. Scala's for is extraordinarily versatile, basically allowing you to work within any monadic context (where a monadic context is some container that implements map/flatMap and some other optional methods.

These "I can't believe its not Haskell" people tend to want to stay with the realm of FP, which is a very useful place to be. Minimising and tracking side-effects and having more expressive and useful types makes more composable software. This is not at all academic, it is an entirely practical result. It does however require you to rethink your approach to writing code, and it really helps to learn a few seemingly difficult things like the typeclassopedia (Monads, Monoids, Functors, Applicatives etc).

Personally I find that the Scala community is generally consolidating around functional approaches, and that this is a very good thing. It is a step beyond a "better Java" and is perhaps more initially intimidating, but it is definitely worth the effort.


no, in 2.9 using a 1 to 10 generator and a foreach was slower than using one using primitives due to the boxing. This is fixed in trunk.

Last I checked (and this may be stale because I've basically given up on Scala except in trying to get the Play Framework to not suck, high-performance code isn't really necessary where I'm using Scala now), foreach over a collection emitted very ugly bytecode that was notably slower than a while loop using Java ArrayList<T>.get(). I'm not aware of that having changed, but I could be wrong.

.

These "I can't believe its not Haskell" people tend to want to stay with the realm of FP, which is a very useful place to be. Minimising and tracking side-effects and having more expressive and useful types makes more composable software. This is not at all academic, it is an entirely practical result. It does however require you to rethink your approach to writing code, and it really helps to learn a few seemingly difficult things like the typeclassopedia (Monads, Monoids, Functors, Applicatives etc).

Thank you very much for the lecture you decided I needed, but as it happens I am a teensy bit familiar with the concepts of functional programming. The more pure forms of it are uninteresting to me except as intellectual exercises. There are very valuable lessons to be learned from FP, but I do not find, and have not found, the pure-FP approach to be the panacea of which it is generally claimed. In particular, the loud noises of the pure-FP crowd about scary side-effects has generally been mystifying to me because it really is not a problem I generally encounter in the code I write now. While it may strain your credulity, somehow this half-blind OOP-using simpleton manages to cobble together comfortably composable code with the meager tools available to him and, perhaps even more shockingly, it doesn't involve one single line of XML. =)

I would agree in a heartbeat that I am a better programmer overall because I have been exposed to functional languages. Obviously, many of the lessons from functional languages lend themselves beneficially to procedural/OO programming: immutability (for obvious reasons), function composition (Haskell's functor laws, as simple as they may be to a FP devotee, are something that often come to mind while I'm designing an API because so many implementations don't compose cleanly!), and careful choice of when and where to exercise side effects (which are, really, the interesting part of 99% of code). But I find the "let's make Scala into Haskell" people way, way overboard (I don't find the morass of monads and applicatives to be doing anything useful) and I generally find their style of programming aesthetically displeasing.

Please don't misunderstand me--the "functional people" are welcome to do whatever they'd like, of course, and I wouldn't tell them to write code the way I choose to! But the post I was responding to was under the "Scala is a better Java" impession, and I think this definitely qualifies as a reason that's not true.

.

Personally I find that the Scala community is generally consolidating around functional approaches, and that this is a very good thing.

I'm glad for you. Different strokes for different folks. And if they want to do that, cool. I've made my peace with that and am sort of hanging my hat on Kotlin and Ceylon as the next place to go from here.

.

It is a step beyond a "better Java" and is perhaps more initially intimidating, but it is definitely worth the effort.

There's no "definitely" about it and while I don't mean to pick on you, that sort of zealotry is exactly why I personally think Scala's future is being that guy in the corner complaining about how crude and unenlightened everyone else is and why aren't they using this thing I like?

I would totally agree with the idea that it is worth knowing a functional programming language or two so you can make a reasoned judgment about where to apply functional principles to all of your code. I would never agree with the idea that it is "definitely worth the effort" to use them in all cases because it's silly on its face. For me, going whole-hog on FP isn't worth a thing because it makes me much slower (and this didn't improve with practice), leads me to write code that I have a lot of trouble maintaining later, and leads me to write code I come to despise because the end result is code that I personally and subjectively find really fugly. So, for me, it's not worth the effort. I know, because I've tried it and found it wanting.

To be honest, I'm a little bit frowny that you'd evangelize it so dogmatically without pausing to think that maybe the person you're talking to does know a thing or two (the automatic assumption that disliking the FP zealots means I don't know anything about it is...interesting) and that it doesn't seem like you've considered the possibility that it isn't all-encompassingly wonderful for everything.


I think the signature of the map function is now the standard example of Scala being too smart for its own good.

  def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That


Yeah, this is really a consequence of Scala trying to do much shit for you automatically with the map function. See, it's not just a map function, but also an auto-conversion function from any generic sequence to any other generic sequence.

What the hell does that mean? Okay, so basically, take your standard map function. Run it on the structure you're mapping over. Great, now you've got your updated structure! Now, you don't just want to return the same type of structure you just had, you also want it to be a different structure, but with those modified elements you had for its contents. Now build that new structure from the thing you just modified (Scala may actually optimize the traversal and construction of the new collection into one pass, although I am not positive). And there's your return value.

It actually gets more complicated due to the Liskov principle and the way that Java handles type erasure, but thinking about that shit just makes me fuckin' tired.

You want to know what the map type signature should look like if the Scala guys weren't going overboard on trying to make things seem outwardly simple?

    class Functor f where 
      fmap :: (a -> b) -> f a -> f b


It does indeed require some explanation to understand why this signature was chosen over a "simpler" signature that might have provoked less mockery from the interwebs. But it's not really that difficult, and it results in a collections library that's better than C#, Haskell, or Python. And I don't know about you, but I use collections a lot.


So, coming from haskell and java, that snippet looks like a nightmare. How is that better than `map :: (a -> b) -> a -> b`?


Well, if you're using Java, you don't have map in the JDK. You can use Guava (or write your own) to get Iterables.transform(). But then, anything you map turns into an Iterable. So often you have to iterate over the whole thing once again to get the data structure you actually need. You could write Collections.map(), Lists.map(), Sets.map(), etc, until you get tired. (Guava does not include these.)

But at this point you're still way behind Scala. What about flatMap, filter, slice, drop, scan, etc? Every collection in Scala has a huge set of methods that work on it. But they're not implemented again and again, because the type system is powerful enough to prove that the generic code that works for Set can be used for List. In contrast, every Java program is full of innumerable primitive operations expressed again and again, so you have to keep reading the steps of every for loop to determine, "oh, this one is doing forall. This one is map" rather than simply reading "forall", "map". Which is why, thanks to the above complexity, Scala can be much more readable than Java.

What about Haskell? The existing standard library is fairly weak on collections. There are not that many implementations, and there's no typeclass that quite captures the idea of a collection: you've got Foldable, Functor, Monad, and possibly some others, which all get slightly different aspects of it. Small typeclasses are nice in that they allow more instances, but often you need some functions that aren't there. There's no typeclass which captures the size() function, for instance. That's mostly a library design issue, which is more tractable than a language issue, but still pretty hard to fix.

There's one additional cool feature of Scala collections which I haven't touched on yet: transformations can result in collections that are different from the original collection type, and the most specific collection that is compatible is used. What does that mean? Say I map a TreeSet from an ordered element type to an unordered type. The result type can no longer be stored in a TreeSet, so you get a more generic Set collection instead. I'm curious what it would take to implement this in Haskell.


So ignoring Java and concentrating entirely on expressiveness & ignoring readability, maintainability, etc... It seems like Collections are the entire kitchen sink of algorithms that map across iterable content. How is that better than more precise types (functors, monoids, monads, foldables)?

For example: what is the benefit of reducing these data structures (and associated algorithms which operate on them) to more generic versions?

> There's one additional cool feature of Scala collections which I haven't touched on yet: transformations can result in collections that are different from the original collection type, and the most specific collection that is compatible is used. What does that mean? Say I map a TreeSet from an ordered element type to an unordered type. The result type can no longer be stored in a TreeSet, so you get a more generic Set collection instead. I'm curious what it would take to implement this in Haskell.

While I agree that this is "cool" that you can do that, I really don't see the use of it except when sketching out ideas when you're not sure what kind of data flow you want yet... but that seems like a problem with designing the program and should be avoided.


In Scala 2.7 the signature for map, defined in Iterable[+A], was def map[B](f : (A) => B) : Iterable[B]

That seems fairly close to the Haskell signature, although somewhat less readable. However, in Scala 2.8, things got more complicated with the addition of the implicit. Consider two functions that I might apply to a collection of integers:

Set(1,2,3,4).map{ _2 } // Set[Int] = Set(2, 4, 6, 8)

Set(1,2,3,4).map{ _.toString } // Set[String] = Set(1, 2, 3, 4)

Nothing surprising here, we provide a method A->B and use it to map from Set[A] -> Set[B] Now lets use the optimized BitSet collection, which efficiently stores integers:

BitSet(1,2,3,4).map{ _2 } // BitSet = BitSet(2, 4, 6, 8)

BitSet(1,2,3,4).map{ _.toString } // Set[String] = Set(2, 4, 3, 1)

Notice that the collection type is BitSet for the function with return type of Int, but plain Set for the function with return type of String. This functionality is brought to you through the implicit parameter.


An asterisk got eaten in your post. You should start lines of code with a couple of spaces to make HN stop doing that.

  Set(1,2,3,4).map{ _ * 2 } // Set[Int] = Set(2, 4, 6, 8)

  BitSet(1,2,3,4).map{ _ * 2 } // BitSet = BitSet(2, 4, 6, 8)


i've never used scala, but it seems to me that that signature is simply ensuring that the type of map is as flexible as possible, and that actual specializations will be inferred by the compiler without the user having to actually specify any part of that.


That's correct.


Now look at the Java Generics FAQ.




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

Search: