I have been a fan of certain functional idioms for quite a while. For instance, given a collection of strings and the need to determine if all of its strings match a certain regex, the typical Java developer would probably do something like this:
boolean allMatch = true;
for (String s : strings){
if (!s.matches(regex)){
allMatch = false;
break;
}
}
if (allMatch){
doSomething();
}
It's ugly and verbose. I think a lot of the reason this is still idiomatic Java is the annoying fact that Java still doesn't have closures, since closures enable concise code like this Scala:
if (strings.forall(_.matches(regex)))
doSomething()
You can hack this kind of thing in Java with anonymous inner classes, but it's ugly and I feel it's often not worth the effort. Sometimes I mess around with Apache CollectionUtils' Predicates to approximate the same idiom, but it always feels gross. On the other hand, every time I give up and write a loop I feel dying a little inside. Scala is much more of a pleasure to use than Java because you can more often just say what you mean.
I like Scala's typing. I like that variables are statically typed, but I can declare a variable's type or leave it off and let the compiler figure it out.
I love being able to represent a function in concise inline notation. Even JavaScript, which is pretty good at letting you treat functions as first-class constructs, makes you say "function" when you declare one. In Scala you can just say "_.matches(regex)".
So, I've been practicing by doing some Project Euler problems in Scala. It's been a great learning experience, but wow it sure has highlighted just how different the typical functional programming style—with all its immutability and linked list manipulation—is from what I'm used to. It's a flashback to my LISP days in college, which isn't necessarily a good thing. (Spoiler: I never really liked LISP.)
Consider a program like this, which is a solution to Problem 7, and took me several minutes to parse:
lazy val ps: Stream[Int] = 2 #:: Stream.from(3).filter(i => ps.takeWhile(j => j * j <= i).forall(i % _ > 0)) val r = ps(10000)
Here's my attempt at an English version of the above:
The value ps is a Stream, which is basically a lazily-populated list of numbers. The first item in the stream is 2, and each successive number is provided by filtering out numbers from the integers (starting with 3) which fit a certain criterion...
The criterion that each number N must satisfy is that all items from the set of numbers in this same stream ps that are less than or equal to its square root are found to follow a certain property...
The property each such number must follow is that N not be evenly divisible by it.
Once the stream is established, that its 10000th member, and you have the 10000th prime number.
It's concise, that's for sure. And slick as hell. But is it readable? I don't know. I mean, I can read it now...if somewhat slowly. I'm used to having a lot more words and symbols per idea at my disposal. So was it just a learning curve I had to navigate, or is the language actually obtuse? I guess I'll see, because I'm going to use it to write my new app.
No comments:
Post a Comment