1. 17
  1. 7

    Last year I had a short Minizinc gig. Mz is a constraint solving language that, for its problem domain, is even more declarative than haskell or prolog. The client had a beautiful spec that captured his complex problem in just a few dozen lines of Mz.

    The problem was performance. Solving for 26 params took a few seconds. Solving for 30 took ten minutes. Solving for 34 took days. He needed it to solve a 40 param input in under an hour.

    I kinda feel like it’s easier to control algorithmic code’s correctness than declarative code’s performance now.

    1. 1

      There are declarative languages that work very well for some problem space. SQL is mostly declarative, so are Mathematica and AMPL. If you apply these tools to problems for which they are not suited, they don’t work well. The problem with Haskell, as far as I can tell is that instead of just admitting that it needed an imperative mode, the designers came up with an elaborate excuse and claimed to have not compromised.

      1. 1

        Haskell has several “imperative modes”: IO, ST, and STM come to mind. All of them work superbly.

        1. 2

          I didn’t comment on how well they work, only on the layer of obfuscatory category theory nomenclature pasted on top of something relatively simple.

          1. 1

            I think most people would agree with you, but would differ on where the “obfuscatory category theory nomenclature” part stops and the “relatively simple” part begins.

      2. 1

        declarative code’s performance

        It’s always been a problem. If something is declarative, always watch out for and/or warn of that by default. I’m not sure how fundamental it is. Too much optimization put into imperative compilers and even verification with stuff like Why3 for a fair comparison. It’s worth further research if not already done.

        Declarative usually trades an intuitive understanding and control of performance for another black box that does the work for developers without knowing context code operates in. Performance and predictability might go down every time that happens.

        1. 4

          Ironically, it should be easier to analyze and optimize declarative code. But since it’s more difficult to understand a compiler and/or runtime, and the performance of a declarative program depends on those, it takes more effort to speed up a program.

          On the flip side, adding optimization to a compiler or runtime improves performance for all programs suffering from the same slowdown.

          1. 2

            SQL optimizers are hard to beat.

        2. 5

          As someone who has occasionally played with Haskell for years and is finally considering to use it for larger projects, this post concerns me. The complexity of monad stacks is a little scary, but I figure the type system makes it manageable. However, if it’s true that monad transformers end up being a source of memory leaks then I’m back to thinking Haskell should only be used for larger, production-level projects by those knowledgeable of GHC internals and edge-case language tricks to hack around inherent problems.

          Can someone with experience comment on the author’s claims? They do seem weak when no specific examples of memory leaks (or abstraction leaks) are provided.

          1. 4

            Do not use StateT or WriterT for a long running computation. Using ReaderT Context IO is safe. You can stash an IORef or two in your Context.

            Every custom Monad (or Applicative) should address a concern. For example a web request handler should provide some means for logging, to dissect request, query domain model and prepare response. Clearly a case for ReaderT Env IO.

            Form data parser should only access form definition and form data and since it’s short lived, it can be simplified greatly with use of ReaderT Form stacked with StateT FormData. And so on.


            1. 3

              Yes it is know to never use RWST+ with a Writer Monad in the stack because of space leak.

              The choices about big-code organisation in Haskell is large. You have:

              • run like in imperative language, everything in IO
              • split using the Handler pattern https://jaspervdj.be/posts/2018-03-08-handle-pattern.html
              • use the MTL, in that case you should not use WriterT (if I remember correctly)
              • use the ReaderT Context IO Pattern
              • use Free Monads, the paint is still fresh here apparently.

              I used MTL-style to make a bot with long living states and logs (using https://hackage.haskell.org/package/logging-effect). It works perfectly fine for many days (weeks ?) without any space leak.

              I now started to go toward the simpler route of the Handler Pattern I pointed out. And, in the end, I tend to prefer that style that is very slightly more manual but more explicit.

            2. 3

              One answer would be to say you leave the monadic stuff at a ReaderT Context IO to deal with the outside world, writing simple non-declarative code to tie together all your pure declarative machinery. Anything wrong with that?

              1. 2

                The comments on this link are especially interesting.

                I’m just learning monad transformers, I hope I understand the controversy about free and freer soon.

                1. 3

                  If you’re getting into architecture in Haskell I’d recommend “Three Layer Haskell Cake” by Matt Parsons too.