For the entire past year I’ve been written Scala code (coming from a Java background). I really liked how you could create simpler and cleaner code, with vals, case classes, map/filter/lambda functions, implicits and the type inference. I’ve used it mostly for an Akka-based application.
This year I’m on a Scala project with a new team, who really like functional programming. They heavily use Scalaz, and the code is filled everywhere with applicatives, context bounds, reader/writer/state monad, even the main method is “wrapped” in an I/O monad. Their reasoning is that this makes the compiler “work for us” in asserting that the code is correct, and each function is free from side effects.
Even so, from my point of view all this syntax really gets in the way of the business logic. For instance, a type of “MyBusinessObject” is fine, as well are types like “List[MyBusinessObject]”, “Option[MyBusinessObject]” or even “Future[MyBusinessObject]”. They all have a clear meaning and purpose. On the other hand, code like:
def method[M[_]: Applicative] = {
case (a, b) => (ca[M](a) |@| cb[M](b)) {
case t @ (ra, rb) =>
if (ra.result && rb.result) t.right
else t.left
}
}
does it add complexity to the program, or is it just me that I’m not used to this way of programming?
12
This has nothing to do with functional programming – you can find this kind of situation in context of any other programming language – developers who love the advanced constructs of “their” language so much that they ignore any common sense about readability and keeping things simple. I have encountered such a situation in C, C++, Perl, Java, C#, Basic, and other non-functional languages. It’s not functional programming that adds complexity to code – programmers do.
Don’t get me wrong, I don’t recommend avoiding advanced language features – but it’s important to find the right balance in the given context. When writing a generic library for the usage of >100,000 developers all over the world, there are different measures to apply as when you are writing an individual report generator just for your local office.
5
I would say that you not being accustomed to the way they code is at least part of the picture. I’m in a similar situation as you (coming from C# into F# and working with people with Haskell background), and while I find it an interesting experience, I do have moments when I’m banging my head against the wall, untangling a particularly convoluted point-free function composition just to get a hang of what’s going on there. It’s a cultural thing.
As for whether this particular code adds complexity to the program – I don’t know. Agreed that a generic piece of code can be complex itself. But it’s a utility, not a part of business logic. If you want to know whether it makes the codebase more complex or simpler, you would have to imagine how it would have to look without that piece of code. It’s often the case with such generic constructs that they’re complex themselves, but it’s a complexity that you can fit on a single screen. In the same time they make the entire codebase a fair bit simpler. This particularly is the case with monads.
Then again, it also can be a case of ‘art for art’s sake’, like @Doc Brown suggests. Can’t rule it out either.
0
I would argue that in general functional programming reduces complexity by eliminating mutable state, thereby reducing the number of cases that must be considered when trying to understand how a section of code works.
However, functional programming makes higher degrees of abstraction possible, and while highly abstract code can be extremely useful it can also be difficult to understand because it is by definition divorced from the context which you would ordinarily use to guide your understanding. Your comment “They all have a clear meaning and purpose” about business objects is undoubtedly true, but is really a reflection of the fact that that logic is very specific to a need and context you already understand. The existence of a construct like Monad allows you to make something very useful with little effort, but the web is littered with pages trying to explain what a Monad is. That’s abstraction for you.
Also, Scalaz was written by folks who had been eating and breathing FP for a long time; they wanted to bring functionality available in Haskell to Scala. In doing so they made no attempt to be pedagogical. Scalaz makes use of a vocabulary and style which seem clear and straightforward to the authors but alien to the uninitiated. Methods which are puzzling to the rest of us seemed so obvious to the authors, given their Haskell background, that they didn’t even warrant a comment.
Furthermore, as a functional programming language Scala has some shortcomings (in part because the JVM has shortcomings) that forced the Scalaz authors to write uglier code in some cases. For example, the lack of general tail call elimination forces the use of trampolines in some code, and the lack of a “kind system” can complicate type signatures.
And finally, Scalaz makes great use of itself. That could be thought of as a sign of its power, but for the uninitiated it can make the source a puzzle — any random piece of code you look at is likely to make use of something else that looks alien to you.
Hang in there. And this might help.
Does it add complexity to the program, or is it just that you’re not used to this way of programming?
Why do you think these possibilities aren’t the same thing?
Well written code can be read by people who aren’t familiar with the specific programming language. Some languages (BASIC, Pascal, etc) can be read and understood by school-children who have never even seen any programming languages before.
If someone who has experience with other languages and experience with Scala (and who I assume has worked with Scalaz for at least a week and has colleagues to explain the trickier things) is still confused; then that’s proof that it has added complexity.
11