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…
Leaving Haskell behind
181–190 of 402 posts
Re: Leaving Haskell behind
#182Basically, 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…
Re: Leaving Haskell behind
#183As 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…
Re: Leaving Haskell behind
#184Earlier 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.
Re: Leaving Haskell behind
#185Re: Leaving Haskell behind
#186I 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…
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
#187This 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…
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
#188Anyone hiring Haskell devs?
Re: Leaving Haskell behind
#189Earlier 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.
Re: Leaving Haskell behind
#190Earlier 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.