Why Java folks should look forward to Scala
blog.dhananjaynene.com
blog.dhananjaynene.com
My first Scala program rolled off my fingers and worked the first time. My second one ran headlong into a number of difficulties; people pointed out that the newest version of Scala added some things (not in the book I had) that would make things easier and they were right... sorta.
Now I've been doing fit-and-finish work on a mixed Scala-Java system written by people with PhD's in computer science and personally I think they'd have been better off writing the whole thing in Java.
It is clearly a better designed language, which of course "borrowed" a lot (90%?) from Java - and then improved it.
I was at a conference back in 1997 where we came up with a wish list of features we'd like in Java for high performance computing. We all knew it was useless to ask this from Sun, but we did anyway.
The first version of C# had everything we asked for in it, and even early versions of C# had a 'delegate' mechanism that was, more or less, method pointers.
C# 3.0 introduced support for functional programming; it's certainly not like CaML, but there's a reasonably fluent syntax for real closures -- with a little work in adding syntactic sugar it's not bad.
LINQ is amazing and people have barely scratched the surface of what's possible like it. There's a system for manipulating "Expression Trees" that lets you do LISP-style metaprogramming.
C# 4.0 added covariant and contravariant inheritance for generics and, unlike JVM-based languages, it works the way it is supposed to without any lame workarounds.
Comparing C# to Java as a ~language~ is like comparing modern italian to ancient latin. Changes happen in Java at a glacial rate after a tortuous process that ensures that when JDK 7 hits the streets it's going to be four years obsolete.
That said, the Java platform has an incredible ecosystem around it; using maven gives me the feeling I'm in Manhattan and can plug into an infinite library of amazing stuff.
The .NET ecosystem, on the other hand, feels like being a crossroads town in the middle of Iowa. Occasionally you get something good like NUnit, but open source offerings are anemic or absent, and commercial libraries often come across like ads in the SkyMall catalog. (Often you can implement the 5% of the library you need yourself in the time you'd spent talking to salespeople)
Offtopic but interesting tangent: that's comparing declined languages ( with nominative, accusative, dative etc cases) to languages that use prepositions and other particles instead. Kind of like comparing static type languages vs. clojure and erlang.
def apply() = l map (_())
println (users filter (_.age < 18))
def addOneToAll(a: Int, b: Int) = (a + 1, b + 1)
The first two uses of () are anonymous functions, the last is a tuple. The only way to tell them apart, best I can tell, is the use of _ turns the scope of the surrounding parens into a lambda.Clojure uses the reader macro #() to indicate an anonymous function.
#(< (.age %) 18)
For me, at least, having a visual clue to distinguish between abbreviated lambda syntax and other kinds of expressions (tuples in the case of Scala) greatly improves code readability.The problem is that then you'll have both the old way and the new way, which will be even more confusing to read and keep straight.
http://www.slideshare.net/normation/scala-dreaded
The visual clue is the method/function signature which you get from the REPL or IDEA scala plugin. I think F#, O'Caml and haskell programmers rely on these heavily also
(There is talk of deprecating one "_" usage)
http://groups.google.com/group/scala-language/browse_frm/thr...
Seems like a good Scala style guide would ban the use of it altogether, or just white list a couple of uses.
Are both {} and () equally valid delimiters for scoping an anonymous function? If so, always using {} would help to avoid confusion with tuples.
{ _ + _ } as a function taking two arguments seems just wrong to me. Shouldn't it be { _1 + _2 }?
No they're not. They are just containers for arguments to a function. In Scala whenever you call a function which takes as it's only parameter another function, you can even replace them entirely with curly brackets:
scala> (1 to 5).map(_ * 5)
res1: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 10, 15, 20, 25)
is equivalent to: scala> 1 to 5 map { _ * 5 }
res3: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 10, 15, 20, 25)
The _ symbol is equivalent to the % in your Clojure example. So both examples above are equivalent to: scala> (1 to 5).map((x:Int) => x * 5)
res2: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 10, 15, 20, 25)
You don't need _any_ characters or visual indicators to declare an anonymous function. Example: scala> def apply(op: Int => Int, y: Int) = op(y)
apply: (op: (Int) => Int, y: Int)Int
Can then be called as: scala> apply(_ * 10, 10)
res4: Int = 100
Or: scala> apply((x:Int) => x * 10, 10)
res5: Int = 100
Or even: scala> apply((_ * 10), 10)
res6: Int = 100
Perhaps your original point stands that in Clojure you have _better_ visual clues for picking out anonymous functions at a glance, but I disagree that you even need them in the Scala code you posted.Yes, this is what I was trying to get at. Thanks for clarifying the examples, though. So, using _ in any expression makes that expression an anonymous function? Is _ by itself the identity function?
Ah, I should have said - the syntax above only works with one-liners. The following wouldn't work:
scala> apply(val y = _ * 50; y * 100, 10)
scala> apply((val y = _ * 50; y * 100), 10)
scala> apply((x: Int) => val y = x * 50; y * 100, 10)
etc.Instead you would need to explicitly surround the method body with curly brackets:
scala> apply((x:Int) => { val y = x * 20; y }, 10)
res18: Int = 200
And in this case, it's obvious what is happening.So it's more like providing a one line expression where a function is expected makes it an anonymous function. The use of _ is simply a shortcut when you don't need to assign parameters to a local variable. It even works with multiple parameters. I.e. compare:
scala> (1 to 5).foldLeft(0)((accumulator:Int,x:Int) => accumulator + x)
res22: Int = 15
To: scala> (1 to 5).foldLeft(0)(_ + _)
res19: Int = 15The result is a Java++ that's worse than C++. It slows down compilation 10x and makes proper tooling (IDE support) 10x hard, while providing no performance advantages. In C++, you can often get nice abstraction idioms (mostly due to template (partial) specialization, SFINAE) with zero performance penalty.
Java folks should just enjoy the superb IDEs (not just 1 but 3) and look forward to Java 8 or something like that (pending the Oracle issues), which will have a reasonable closure implementation ((JSR-335 looks OK to me), which would make much of the user (vs library/framework writer) side verbosity go away.
People use Java because its stability, compatibility and ecosystem, so C# wouldn't be a viable choice for them but an interesting thing to watch and learn lessons from.
It's a Java-compatible JVM language that doesn't change stuff up too much, just addresses some shortcomings of Java.
However, it is also incredibly confusing to learn/use at times because it is still maturing and such. The tooling isn't as good as Java.
The web frameworks for Scala are actually both awful and awesome. Lift is beautiful, but designed by a nerd and won't ever get Rails-like traction. Play is fantastic, but it's just as fantastic for Java, so it almost negates the desire for Scala... almost.
Ultimately, Scala is a breath of fresh air largely because most of modern Java focuses so heavily on Java EE. Scala provides a clean break from that (if that is what you're looking for).
Scala is going to be a niche JVM language. It will never reach the usage levels of Java/C#. It might hit Groovy levels of usage, maybe. I doubt it will ever be as big of a hit as Ruby/Python.
Tools are great and learning new ones is a good thing. But articles like "X should look forward to/embrace Y" are short sighted unless they take into account the simple fact -- what is the end goal. If it is shipping product, the whole team needs to be on board from design, implementation, running, and maintenance.
Obviously there are no silver bullets that make software development easy. But after having written a handful of high performance, high availability systems in Scala, the benefits over the other alternatives on the JVM are so evident to me, it's easy for me to get frustrated that they aren't obvious to everyone. Of course we still have occasional bugs, and sometimes they are hard to diagnose. Just exactly like Java, and any other language you care to choose. Bugs are inevitable. But there are a couple of things that make Scala stand out, and I'm a bit surprised at how fickle the community is about this language. One day everybody loves it, the next day, everybody seems to hate it because it didn't make programming easy. Both are extreme; as engineers and scientists we should approach cautiously and methodically; doing so, in my view, makes a few truths unescapable:
1. Scala is far less code than any statically typed language on the JVM. Less code means fewer bugs.
2. The Scala collection framework is a tour de force. If you can't see that, you should keep looking. Seriously, it is quite simply amazing for anyone coming from Java -- there is nothing like it. Not since I first got a taste of STL have I enjoyed the hybrid sensation of aesthetic awe with the 120M volts of power. It is just so clean, so orthogonal, and such a pleasure to work with. It is hard to go back after you have gotten used to this thing.
3. Scalacheck takes testing to another level. When I take the care to properly use a tool like Scalacheck to cover what I'm working on, the number of bugs goes as low as I've seen on any platform, anywhere. I have live code which I am pretty damn sure doesn't have any bugs. The only code I've written in 20 years of doing this that doesn't have any "not critical enough to fix" bugs, is written in Scala. I cannot tell you how satisfied that makes me feel -- it's a long time since I considered that possible.
It is difficult to argue against the idea that there are too many ways to do things in Scala. There is more than one way to do a lot of things, and I personally find the right way for me to be fairly evident after writing a few thousand lines of code. As an engineer, I'm all for constraints to make my job easier. But at the same time, since writing code is a very indirect way to express ideas, it can be nice to have an expressive, fastidiously symmetric, means to do so. The symmetry in Scala is breathtaking ... the edge cases in Scala are an order of magnitude less bizarre than the edge cases in Java. After a few months of Scala, the pathological examples touted in these comments look completely obvious. They are doing a powerful thing. Learning to think in the language is required if you want to be fluent. And what I love about learning to think in Scala, is the extremely intelligent hand of its creator is evident everywhere.
And that is the problem. Most people will never reach the state of having writing a few thousand lines of code, because they are to confused to begin with.
In my mind, consistency is a prerequisite to a short learning curve. How can it be short if what you have learned in the past is constantly questioned along the way?
When the experts provide multiple ways of achieving the same thing, they force the act of making the choice upon everyone, which is a huge barrier for beginners. Scala could attract a lot more beginners, if it didnt make them think about choices at every step of the way.
It's easier to better engineer complicated things in simple ways when you are less encumbered by non-essential complexity. Once you surmount the learning curve, Scala brings a lot. If you can't or won't put enough in to make it, well, sure, it won't be for you. Personally I like solving problems that are hard enough to benefit from Scala's expressiveness. But I completely concede that for lots of people, there's just no reason not to keep on doing Java. Or Cobol for that matter.
The Scala syntax, I must say is not as readable: def apply() = l map (_())
def apply() = l.map((f) => f())
Is that more clear?