← Back to context

Comment by gambler

6 years ago

>Actually, not encountering this style in code-base which is solving a complex problem is a massive warning sign for me.

There is a fine line between capturing some common meaning in a reusable type (e.g. Optional<T>) and spending most of your time crafting a straight-jacket of type restrictions that are supposed to thwart some imaginary low-level bug in the future.

Java is actually a great example of how this mentality backfires in real life. The language, from day 1, had a zillion ways to force someone else to do something they didn't want to. Abstract classes. Private methods. Final classes. Etc. It was supposed to ensure good design, but in practice just led to the endless reenactment of the banana-gorilla-jungle problem. (https://pastebin.com/uvr99kBE)

At the same time, the language designers didn't add such basic features as lambdas and streams until 1.8. This lead to creation of ungodly amount of easily preventable bugs, since everyone was writing those awful imperative loops.

Banana-gorilla-jungle problem results from OOP. This article (and presumably, the GP) is describing encoding domain-driven logic in a statically-typed functional language, and thus the OOP issues are not a problem.

Functional languages don't carry around their environment with them, so you just get a banana. No gorilla or jungle.

  • Oh, it does have some banana-gorilla-jungle problem, it's just not at the same place.

    If the types are computationally equivalent, there is a plentora of type classes you must inherit, there is a lot of packing and unpacking to make them work with standard libraries (how do you print an address?), and if the values are not completely separable, there are plenty of conversion issues.

    It is an improvement, no doubt about it, but the main benefit of the languages with advanced type systems is that you can refactor code easily to add them only to the places where it's a win, only after your code is complex enough to make it a win.

  • >Banana-gorilla-jungle problem results from OOP.

    Not really. There are specific design decisions that Java made which enabled this issue to manifest. I've recently took a foray into Smalltalk and it doesn't have this problem, because methods depend on object interfaces rather than class types. AFAIK, OCaml also got this issue right while keeping static type checks - they decoupled classes from types.

    >Functional languages don't carry around their environment with them

    It is entirely possible to recreate the issue in a functional language, as long as there are sufficiently powerful mechanisms to enforce type safety during compilation. In fact, if you don't understand this and follow the advice in the article too far, that's exactly where you will end up. You will have a jungle monad containing gorilla monad holding hostage the banana data structure the user actually cares about.

    • In the off chance you're reading this 9 days later, I'd like to push back against this response (working on an OCaml project at work, actually).

      > AFAIK, OCaml also got this issue right while keeping static type checks - they decoupled classes from types.

      If you mean that the types correspond with OCaml classes themselves and not the objects that classes create, yes, that's correct. But it sounds like you might mean the opposite of that. In any case, people rarely use the O part of OCaml much. Most people find they prefer the first-class modules for almost everything that needs greater modularity than what functions provide. I've found that the only thing that OCaml object system is good for is modeling/wrapping existing OOP or prototypal APIs, like certain XML ones, or some web APIs.

      >You will have a jungle monad containing gorilla monad holding hostage the banana data structure the user actually cares about.

      While it's true that monads are a way to make normally explicit environments implicit, monads aren't really what the book I'm talking about is about, nor this article.

      Yes, monad-heavy code could (with effort) end up with an application monad displaying problems similar to the banana-gorilla-jungle problem. I've frankly never seen monadic code like that, but perhaps you have.

      Instead, what this article is advocating for is simply modeling your data correctly. Really, this is a universal programming theme, that helps a lot regardless of programming language. But the structures available in statically-typed functional languages are more powerful and able to model domains with greater granularity. If you're familiar with the saying "make invalid states unrepresentable", then you understand what I mean.

Java has just had a terrible steward in Oracle. Plus the community seems to like boilerplate code (see: Angular for more of that if you're into javascript).

I switched to C# as my main language specifically because they were gaining awesome features like generics, lambda, LINQ. Since then it just got better and better.

In the C# world you seem to encounter systems where the logic is either in:

* UI event handlers * Methods on helper classes which consume a model generated from a database. * Domain modeled with classes with the logic split between the classes themselves and services which consume classes.

In the first two cases the code tends to have a massive sprawling dependency tree which makes it harder to maintain. In the latter case you've usually got a better chance of having properly isolated code. Even if the isolation isn't super you at least have the logic well organised.

it's extremely useful especially at interface level but also in day to day coding, pity the people whose method signature is (string, string, string) because no ide can save them from the churn.