18

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?

Peter Mortensen
  • 1,050
  • 2
  • 12
  • 14
Luciano
  • 562
  • 4
  • 10
  • 6
    What's your objectively answerable question? – Deer Hunter Apr 04 '14 at 05:31
  • 1
    Looks like it leads to a ton of code with one or two letter variable names. Looks like APL a bit. That is not a complement. – user949300 Apr 04 '14 at 06:09
  • related: http://programmers.stackexchange.com/questions/158715/are-small-amounts-of-functional-programming-understandable-by-non-fp-people/ – superM Apr 04 '14 at 07:49
  • 1
    If that method is really called "method" then dear god. On the other hand, highly abstract functions can often be very difficult to understand the workings of, but easy to understand the results of. Without knowing what that's trying to do, though, I lean towards suggesting abstractness wankery. (A close cousin to type wankery, which I guess you also have lots of) – Phoshi Apr 04 '14 at 08:13
  • 3
    I started playing with Haskell last year. It looked incredibly complex at the time, when I didn't understand concepts like currying, functors, monads, etc.. Haskell is similar to Scalaz in that it has a lot of short, symbolic functions, like `>>=` and `<$>`, which mean nothing until you know what they do. After learning what they mean, however, they read very naturally and quickly to me now. Not really an answer, just my objective experience with things like this. I use Scala as well, but have no experience with the Scalaz library. – KChaloux Apr 04 '14 at 12:24
  • 3
    You are just unfamiliar with the idioms. @user949300 short variables are not really a problem for lot of functional code (think about Math style conventions!). Also read Tony Morris' blog for a more in-depth discussion of what better conveys meaning, types or verbose variable names. – Andres F. Apr 04 '14 at 12:49
  • 3
    @user949300: short variable names are preferred locally, that's the same in the functional and imperative worlds (you wouldn't write `for(i=0; i<7; ++i) { trivialOperation(i); }` with some awkward `trivialOperationCount` variable, would you?) Now, functional programming languages with pattern matching will sometimes introduce some more variables where you'd just write out accessor method calls in OO. The result is generally more concise; perhaps a little less self-explanatory, but looking up the data declaration normally makes it clear quickly. Static typing helps a lot, it's not like in APL. – leftaroundabout Apr 04 '14 at 18:56
  • @leftaroundabout and Andres. Sure, I'll use i and even j as a loop counter. But in that method, _every_ variable or function call is one or two characters. Say what you want, but it goes against years and years of programming experience and best practices. – user949300 Apr 04 '14 at 22:58
  • @user949300: but apart from `ca` and `cb` they're all introduced no more than two lines above from where they're used. The problems with short names arise mainly when there's potential for confusion, and tight scoping prevents that. And because these short names contribute to conciseness, you can more often keep the scopes so small. (Sometimes it's even better to avoid introducing local variables completely by using [point-free style](http://en.wikipedia.org/wiki/Point-free_programming#Functional_programming).) – leftaroundabout Apr 04 '14 at 23:13
  • Indeed the only problematic names are `ca` and `cb`, but they are apparently vital to what this code does. That's a problem. What I want to know is, what would this look like written in some other style of programming--then we can see whether functional programming made it more complicated. – David K Apr 05 '14 at 00:44
  • @DeerHunter maybe I wasn't able to objectively rephrase the question, according to the answers, it was understood. – Luciano Apr 05 '14 at 02:35
  • @Phoshi I changed the name to generic method because of NDA – Luciano Apr 05 '14 at 02:35

4 Answers4

37

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.

nobody
  • 848
  • 7
  • 12
Doc Brown
  • 199,015
  • 33
  • 367
  • 565
  • 8
    It also has a lot to do with the community. For a developer with a Java or C# background, the code is barely understandable (and his/her community wouldn't understand it either). But if you write Haskell, for example, and you don't use monads, applicatives, functors and so on, you are baffling that language's community. The "naturalness" of code is not inherent, but relative to its community and established practices. – Andres F. Apr 04 '14 at 12:55
  • 1
    This is hard to see because most of us come from imperative backgrounds, which sometimes leads us to make the wrong assumptions about what's natural. – Andres F. Apr 04 '14 at 12:56
  • just look at the SLT C++ library, that could be written to be so much more readable for the amateur – ratchet freak Apr 04 '14 at 15:22
  • @ratchetfreak: I guess you mean STL. I think this is a very good example (and indeed, I had this in mind in my answer). Using template meta programming makes lots of sense when you are an STL programmer, because it makes the STL more reusable. And the people having to maintain that code are normally used to template metaprogramming, too. Using template metaprogramming in an STL-like fashion throughout your standard business application can easily lead to overcomplicated, hard to maintain code. One can surely find (rare) cases where TMP is fine even in business application, of cours. – Doc Brown Apr 04 '14 at 15:34
  • @DocBrown yeah dyslexia kicked in at the wrong time, but honestly if I had half a mind to (and much more time than I have now) I could rewrite many of the function bodies to be much more readable. – ratchet freak Apr 04 '14 at 15:37
7

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.

scrwtp
  • 4,532
  • 1
  • 24
  • 29
6

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.

AmigoNico
  • 169
  • 4
-3

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.

Brendan
  • 3,895
  • 21
  • 21
  • 11
    This is simply not true: `"Well written code can be read by people who aren't familiar with the specific programming language."` – Andres F. Apr 04 '14 at 12:57
  • @Andres: It is true... to a point. Well written code will separate the business logic from the implementation details, and the business logic should be readable, because most of what it is doing is making straightforward calls to well-named helper functions. Those helpers may use all kinds of language features, of course, and require heavy experience with the language and libraries to understand. – Ben Voigt Apr 04 '14 at 17:14
  • 4
    I believe the following is idiomatic APL: `x[⍋x←6?40]`. What do you think it does? I sure wouldn’t have known… – bdesham Apr 04 '14 at 18:15
  • @AndresF.: It's true by definition. If people who aren't familiar with the specific programming language (but are familiar with other programming languages) can't read it, then it's not well written code. How else do you think "well written" should be defined? – Brendan Apr 05 '14 at 01:50
  • @bdesham: I believe it's hard to write well written code in some languages. For example, for assembly language often you need a comment on every single line, and this means it's easy to write badly written code in assembly. Other ("better") languages make it easier to write well written code. – Brendan Apr 05 '14 at 02:07
  • Also note that the opposite of well written is "obfuscated" - code that's so badly written that even people who do know the language well have trouble reading it. Basically there's a range of programmer familiarity (from never seen the language before to expert in that language) and the "well written to obfuscated" range maps directly to the range of programmer familiarity needed to read it - the more well written something is the less familiarity the reader needs, and the more obfuscated something is the more familiarity the reader needs. – Brendan Apr 05 '14 at 02:17
  • 3
    @Brendan Only within the same paradigm (and even then, sometimes not). For example, Prolog, Java, and APL are so different that I would argue that if you only know one of those (and no other languages), you cannot read the other two, no matter how well you know the first one. (Seriously, in bdesham's example, how the devil are you supposed to interpret "christmas tree" if you don't know any APL?) – Izkata Apr 05 '14 at 04:19
  • @Izkata: If APL was a well designed language it'd be relatively easy for programmers to deduce the meanings of its symbols. If a programmer writes well written code then they can overcome the problems of badly designed languages (e.g. comments), and turn something that would've been confusing into something less experienced programmers can learn from. 1/2 – Brendan Apr 05 '14 at 10:26
  • @Izkata: Paradigms have nothing to do with this - you can write bad code using any paradigm in any language. All of the things people have trouble with in different paradigms aren't because the concepts are hard, but are because the syntax the language expects isn't good. 2/2 – Brendan Apr 05 '14 at 10:28
  • 1
    Brendan, your definition of well-written is nonstandard. Well-written is always relative to the language and its community. A program in language X is well-written if it's not buggy, it's efficient and clear... for the given audience! This applies to written language in general, by the way: always know your audience. What is suitable for (say) a scientific paper is probably not suitable for an email to your mom. – Andres F. Apr 05 '14 at 13:31
  • @AndresF.: Agreed (despite being both correct and logical, my definition of "well-written" is nonstandard because the standard being used is wrong, broken and bad for everyone). Almost every programmer I know is fluent in 2 or more languages and "vaguely knows" many more. It is a severe mistake to assume your target audience can be narrowly defined as "those that commit their lives to a single language". – Brendan May 29 '14 at 13:16