Live data from Hacker News

Leaving Haskell behind

journal.infinitenegativeutility.com

181–190 of 402 posts

Re: Leaving Haskell behind

#181

First off, learning Haskell's like trying to decipher an alien language. If you're used to plain ol' if-else loops and straightforward variable assignments, prepare to have your brain twisted into knots. Haskell's got monads, and no, they're not some new type of space monster – they're these weird abstract things that'll leave you scratching your head and questioning your life choices. Now, I know we all love librari…

[flagged]

Re: Leaving Haskell behind

#182

Basically, the author's criticism is that the language is too powerful, too expressive, people try very abstract things, tooling is bad and no one cares about the language. There is something sinister in this - first, in the author's lamentations about bad tooling. Other languages require linters, formatters, static analysers, etc. because the language's built-in features and type system are sub-par. In Haskell, that…

Some programs live for decades . Some programs get worked on after the original authors are gone. Code is written once, but (if the program is worth keeping) read many times. Optimizing for "easy to write" is optimizing the 10% and ignoring the 90%. Optimizing for "easy to read and understand by someone who is not the original author" is critical for important, long-lived programs. It's not "laziness". It's understan…

The thing to optimize for is letting experts communicate with each other clearly and concisely. I’m only going to be a newcomer to a system for a short time, and any work while I'm ramping up is not going to be very valuable.

Re: Leaving Haskell behind

#183
post #20

As someone that has also written haskell for about a decade and moved away from it as a breadwinner recently (but for other reasons - I simply wanted to filter job offerings based on social utility rather than language stacks), I definitely agree with the author's first point: the Haskell community values learning extremely strongly. That's great because you work with curious people that have always something to teac…

Haskell's tooling has put me off ever really trying to get into it again. However, I've dabbled in Purescript a few times and it always seems to offer a pretty smooth experience, especially if you're familiar with JS tooling.

Re: Leaving Haskell behind

#184

Earlier quoted context omitted.

Perhaps, but what you may not understand is that not ALL developers _want_ a purely functional language. For some, things like Kotlin hit a sweet-spot. One can lean a bit more into a functional style, or they can lean more into an OO style and it's acceptable. Some are very interested in thinking in terms of Functors, Applicatives, Readers, etc... some just want map/filter/reduce. That's what the Streams API did for…

Kotlin doesn't get enough love. It gets derided by some Java developers for being too cutesy and sugary and it's not talked much about by the kinds of people who love to talk about Haskell, Lisp or Rust (no shade to these languages), but to me it's the most pragmatic language I've used so far.

Speaking as a lover of Scala who has seen Scala codebases go wrong in the cliché ways, Kotlin would be the first language I would consider if I were starting a new commercial software codebase today. Kotlin seems to have enough of the pragmatic elegance of Scala to get the job done with clarity and accuracy, without the curse of attracting the compulsive intellectual thrill-seekers that will ruin your Scala team if you accidentally hire one.

Re: Leaving Haskell behind

#186

I had a pretty similar experience: spent a decade (2007-2017) working professionally in Haskell and just got completely fed up with the state of the language, ecosystem, and community. I migrated most of my new work to Ocaml and haven’t looked back. For me I think the failure of the Haskell Prime effort to establish a successor standard to Haskell 98 was a big factor: the language and ecosystem became more chaotic an…

For the reference, 2017 was the year of GHC 8.0. Since your decision to never look back there were a lot of good things.

The standard didn't come out because of some failure to make it. It was mostly the lack of interest that killed it. I wouldn't be betting that some alternative universe where Haskell Prime pulled through had a noticeable increase of adoption because of this.

Looking at proposals, arguments "from standard" don't tend to generate enough support. What wins hearts is alleviating someone's pain without taking disproportionate externalities.

Re: Leaving Haskell behind

#187
post #115

This part is interesting: A good concrete example here is a compiler project I was involved in where our first implementation had AST nodes which used a type parameter to represent their expression types: in effect, this made it impossible to produce a syntax tree with a type error, because if we attempted this, our compiler itself wouldn't compile. This approach did catch a few bugs as we were first writing the comp…

The former is referring to representing an AST using a GADT in order to include the typing discipline of the target language in the host language. For example:

  data Term t where
    Num :: Integer -> Term Integer
    Bool :: Integer -> Term Integer
    Add :: Term Integer -> Term Integer -> Term Integer
    IsZero :: Term Integer -> Term Bool
    IfThenElse :: Term Bool -> Term a -> Term a -> Term a
With this AST, you can express well-typed programs like `Add (Num 2) (Num 3)`, but the Haskell type system will stop if you express an incorrectly-typed program like `Add (Num 2) (Bool False)`.

The "Trees That Grow" paper, on the other hand, is about reusing the same AST but gradually adding more information to the nodes as you progress through the compiler. For example, you might want to start with variable names being raw strings (so that a term corresponding to `lambda x: lambda x: x` looks like `Lam "x" (Lam "x" (Var "x"))`) but eventually replace them with unique symbols so that shadowed names are non-identical (so that under the hood it looks more like `Lam 1 (Lam 2 (Var 2))`, although in practice you'd want to keep the old name around somewhere for debugging.)

One way to accomplish this is to introduce an explicit type-level notion of compiler phases, give your terms a type parameter which corresponds to the phase, and use the phase to choose different representations for the same nodes:

  data CompilerPhase = Parsed | Resolved
  
  data Expr (phase :: CompilerPhase)
    = Lam (Name phase) (Expr phase)
    | App (Expr phase) (Expr phase)
    | Var (Name phase)

  type family Name (t :: CompilerPhase) :: *
  type instance Name Parsed = String
  type instance Name Resolved = Int
Using this example, an `Expr Parsed` will contain variables that are just strings, while an `Expr Resolved` will contain variables that are integers, and you can write a pass `resolve :: Expr Parsed -> Expr Resolved` which just modifies the AST. (This is a toy example: in a real compiler, you'd probably want to create a new type for resolved variables that still keeps a copy of the name around and maybe some location information that points to the place the variable was introduced.)

Re: Leaving Haskell behind

#189

Earlier quoted context omitted.

You should generally be writing code against typeclasses, not a particular monad transformer stack. For example: fibonacci :: MonadState (Int, Int, Int) m => m Int fibonacci = do (prev, prev2, n) 0 then put (prev + prev2, prev, n - 1) >> fibonacci else return prev2 concreteFib :: ReaderT String (StateT (Int, Int, Int) (ExceptT String Identity)) Int concreteFib = fibonacci

You misunderstand my problem. Add a logger to that fibonacci function. Potentially EVERY usage site now has to change, maybe even multiple layers. Adding a log in most languages is a local transformation. In Haskell it isn't, it can have codebase wide consequences.

Debug.Trace has a lots of stuff for that. You can even generate charts from that when using eventlog-enabled runtime.

Re: Leaving Haskell behind

#190

Earlier quoted context omitted.

Does every usage site have to change? You would alter fibonacci to be: fibonacci :: (MonadLogger m, MonadState (Int, Int, Int) m) => m Int fibonacci ... and now of course all callers must support MonadLogger. But instead of using the MonadLogger (or any mtl constraint directly) you should just be constructing an abstraction boundary with a type class synonym: class (MonadLogger m, MonadState s m) => MyMonads s m and…

I have seen this in the wild. The result often is that every function has a kitchen sink MyMonads constraint of which it only uses a tiny subset. It's death by a thousand cuts. If you make such a class for every monad combination you get insanely large amount of classes. It's simply unworkable. Which is why you get the kitchen sink monad pattern.

And what's wrong with the kitchen sink monad pattern? I've certainly used exactly that. And I have no problems with it.
Post reply on HN