Live data from Hacker News

Pain Points of Haskell

dixonary.co.uk

181–190 of 322 posts

Re: Pain Points of Haskell

#181

This is a great post. However, there are so many more things that should be on here! If I could suggest just one, it would be lazy evaluation by default, which makes it exceedingly hard to reason about time and space complexity. Also relevant is the author's article on problems using Haskell on Arch Linux: https://dixonary.co.uk/cabal-2020

FYI: As of the time of this reply, https://dixonary.co.uk/cabal-2020 returns a 404.

Re: Pain Points of Haskell

#182
post #102
post #42

Earlier quoted context omitted.

> OCaml's is also excellent but not immediately obvious (their docs have improved a lot) Interesting! The last time I tried OPAM, it actually seemed more frustrating than Cabal! Maybe it's improved? Last time I tried OPAM it would install packages globally by default, and avoiding that was a confusing process, when that should be the default behavior. Cargo (while not perfect) has really nice defaults out of the box…

A bit off topic, but is Haskell viable for production? Or is it just really intended as an experiment? Is it worthwhile to jump into Haskell now, or perhaps one should look into Idris or Agda? Any companies using Haskell where it has proven a distinct advantage? Ocaml and SML (the former being sadly quite unpopular these days) are an order of magnitude simpler than Haskell. They are easy to master. I have been quite…

I've got a REST API in production linking to postgres and rabbitmq and I'm very happy with the performance and maintainability. The ecosystem is a bit more bare than with other languages like node, python or java but in exchange the paradigm and type-system lets you build safe and well isolated code. In my opinion it's also quite easy to read despite being terse.

Despite the complaints the build tooling is good, and there are decent plugins for IDE capabilities although they're a little bit resource hungry.

My biggest problem is the lack of component libraries, for example I wanted to use SMTP a little while ago but couldn't find a library compatible with the latest GHC so ended up writing a python script connected to rabbitmq.

For business logic involving timezones, currencies, API calls, calculations etc. you're pretty well covered though.

Re: Pain Points of Haskell

#183
post #62
post #2

Warning possible flamebait: could Clojure be the solution?

I've tried to get into Clojure multiple times and it feels quite unergonomic to program. I think my brain is not correctly shaped for its constructs, whereas Haskell fits my mental models perfectly.

Would you say you are a top-down thinker/programmer?

Re: Pain Points of Haskell

#184

Earlier quoted context omitted.

A monad is an abstract interface for sequencing side effecting operations in a way that guarantees referential transparency. They do this by hiding the extraction of the value contained in the monad and passing that value to a user supplied lambda that takes a value of the type of the value contained in the monad and returning a new monad containing a different type. Thus, you cannot do step one after step two, becau…

What is "referential transparency"? > "by hiding the extraction of the value contained in the monad and passing that value to a user supplied lambda that takes a value of the type of the value contained in the monad and returning a new monad containing a different type" I'm not sure I follow. Is this like saying I have a function: let myFunc = (arg1: SomeType): NewType => {} And it takes SomeType, and returns NewType…

> I'm not sure I follow

Imagine you have a context (the monad), which contains a value of type `a`...

> "by hiding the extraction of the value contained in the monad...

Means, you don't need to care about how we get the value out of the context, it will just be done for you. That's part of the monad's job. Each different type of monad may do it differently (a List will do it many times, an Option will do it zero or one times, Async promises to give you a value, etc. ).

> "... and passing that value to a user supplied lambda that takes a value of the type of the value contained in the monad ..."

So, we have a function called `bind`, which is part of the interface for monads ... it takes the original context (monad) as well as a lambda as arguments.

Below is the signature: `m a` which is the original monad value, (a → m b) is the lambda, and `m b` is the return value, which is a monad with its contextual value type changed from `a` to `b`

    bind :: m a → (a → m b) → m b
Where you see `m a` and `m b`, they're generic types which are parameterisable on both the inner and outer type, the inner type `a` is fairly usual, but the outer-type `m` is a higher-kind and can also be parameterised, `m a` could become `List a` (which is like List in C# or Java).

So, if you squint, you might be able to see that the `a` comes from within the `m a` context, gets passed to the lambda, which needs an `a` argument and returns an `m b`, then that `m b` is returned as the final result.

Each type of monad has its own implementation of `bind`. So, for a List the bind operation looks like this:

    bind :: List a → (a → List b) → List b
And so you should see that for Lists each `a` will be extracted from the first argument (so we'll have zero or many `a` values), each `a` is passed to the lambda which will return a `List b` (and so we have zero or many `List b` values), and those lists of `b` will be concatenated to give a flattened result of `List b`. And so, that's the behaviour if the List monad. Each monad will have its own rules, but must follow the signature of `bind`.

> "...and returning a new monad containing a different type..."

This just means the `a` turns to a `b` (but the `m` stays the same). Essentially it means you can do some kind of mapping of the contextual value(s) to return a new type. So, if you were to map Ints to Strings the bind signature would look like so:

    bind :: List Int → (Int → List String) → List String
In practice you can see how the lambdas returning `m b` allow for chaining of computations. The code below visits the items in two lists (listA and listB) and sums them. The lambdas close over each other to create a context for the `return` at the end of the process. Which is very close to how scoping works in imperative languages.

    bind listA (\a → 
        bind listB (\b → 
            return (a + b)))
The key to any monad is its bind function, that's where its behaviour is. But fundamentally, it's a contract, or interface which forces ordering of computation... the values returned by the lambda (which are monads themselves), clearly can't be evaluated until you evaluate the first argument to get the `a`.

And so, `m a` must run before `m b` ... combine that with `do` notation and you'll get something that looks and feels a lot like imperative code, but all of the side-effects are encapsulated and declared, and all of the logic is pure (referentially transparent).

The example above with `do` notation is:

    a ← listA
    b ← listB
    return (a + b)
Which is essentially equivalent to a nested for loop without the ceremony.

Re: Pain Points of Haskell

#185

I personally don't understand the hangup on the existence of an IDE. Don't get me wrong - IDEs are great, especially for beginners. But "one-editor-per-language" is an increasingly outdated mode of thinking. The culture shock of having to download a whole new IDE for a new language is a distinct negative. Beginners benefit from new languages slotting neatly into existing tools, which is exactly what the language serv…

Personally I don't want an IDE, just solid integration with existing editors.

I didn't fully understand the power of this until I started working with Rust and VS Code. It's a really first-class experience. Hints and type errors show up so immediately and responsively in the editor with complete contextual information that I can develop essentially an entire library or application without ever running a build once. It makes me feel like I can just tear through hundreds or thousands of lines of code because I'm getting so much constant feedback on everything I write.

Re: Pain Points of Haskell

#186
post #6

As someone who stopped following Haskell world few years ago, this was quite interesting read. I wonder what is the state of ecosystem (libraries) now: a few years ago one of my friends complained that for most basic tasks there is some library but usually half-working and abandoned. However: I think the point about monads is fundamentally misguided. Monad is an abstract concept and trying to explain it in non-techni…

I fully grok monads. I’ve done enough reading and usage of them to understand them.

They are not a useful abstraction for programming. They’re mathematically correct, but that’s not the same as what an industrial engineer needs.

The big warning sign is monad transformers. Alone, monads are totally fine, but the issue is that you rarely want one. So you end up with this unwieldy tower of transformers that would make an enterprise Java engineer worried.

Re: Pain Points of Haskell

#187
post #42

As a guy who casually (definitely not in-depth) reviewed working with Haskell about 2 years ago I'd say the most relatable part of the article to me is: complexity in tooling and unnecessary tension when working with libraries. Also the String situation (several types of them). A lot of the other points could be addressed but the community's seeming unwillingness to tackle everyday productivity sends to me the messag…

> OCaml's is also excellent but not immediately obvious (their docs have improved a lot) Interesting! The last time I tried OPAM, it actually seemed more frustrating than Cabal! Maybe it's improved? Last time I tried OPAM it would install packages globally by default, and avoiding that was a confusing process, when that should be the default behavior. Cargo (while not perfect) has really nice defaults out of the box…

By default, opam installs everything into the current "switch". Typically you have switch one per compiler, but you can create one per project. You can also create a "local" switch directly inside your project directory: https://opam.ocaml.org/blog/opam-local-switches/

Another option is to use the duniverse tool (https://github.com/ocamllabs/duniverse). That downloads all dependencies to the project directory, and then builds them all together as one giant project. That makes it very easy to make changes across multiple libraries at once, while keeping fast incremental builds.

And a final option is to build projects with Docker, which allows you to have separate versions of non-OCaml packages too.

Re: Pain Points of Haskell

#188

Earlier quoted context omitted.

It is in your interest to have multiple string-like datatypes in a lazy language, especially when some of them are not real strings, but rather streams of binary data. https://mmhaskell.com/blog/2017/5/15/untangling-haskells-str...

I agree, although the arguments in that article aren't the strongest. In particular: The main reason given for 'String' being slow is that it's immutable and hence leads to many duplicate Strings being created. In fact, the main reason Strings are slow is that their linked-list structure has a lot of overhead, and may be scattered around in memory. For example, the String "abc" will be represented something like this…

Sounds a bit like the Rope data structure from 1995: https://en.wikipedia.org/wiki/Rope_(data_structure)

Although instead of a list of chunks, a rope is an immutable binary tree of chunks, so more editing operations are efficient, eg insertion or deletion in the middle of the rope.

Abseil library contains an open source C++ implementation of a similar structure called a Cord: https://github.com/abseil/abseil-cpp/blob/master/absl/string...

Re: Pain Points of Haskell

#189
post #160
post #133

Earlier quoted context omitted.

Well, I don't know the details of your personal experience, but I'm confident to declare that the vast majority of the Haskell community has never used Nix and has no particular intention to.

Nix has managed to permeate almost every repo I work with. Only a minute ago I realised a Make file had had nix-shell stuff added to stack build. There is no escape, only Nix.

Intriguing. Are these public repos? Care to share some examples?

Re: Pain Points of Haskell

#190
post #42

As a guy who casually (definitely not in-depth) reviewed working with Haskell about 2 years ago I'd say the most relatable part of the article to me is: complexity in tooling and unnecessary tension when working with libraries. Also the String situation (several types of them). A lot of the other points could be addressed but the community's seeming unwillingness to tackle everyday productivity sends to me the messag…

> OCaml's is also excellent but not immediately obvious (their docs have improved a lot) Interesting! The last time I tried OPAM, it actually seemed more frustrating than Cabal! Maybe it's improved? Last time I tried OPAM it would install packages globally by default, and avoiding that was a confusing process, when that should be the default behavior. Cargo (while not perfect) has really nice defaults out of the box…

Maybe you tried it before v2? Imho opam now is very usable, lets you be productive for 95% tasks after reading a 5min introduction, and generally gets out of your way.
Post reply on HN