4 ms·
The issue is not language semantic. The issue is readability. Having the best feature set in the world is useless if the code produced by others is a pain to de
by RandomThoughts3 2y ago
The issue is not language semantic. The issue is readability. Having the best feature set in the world is useless if the code produced by others is a pain to decipher.
Haskell disqualified itself for general programming when its community decided that point-free was desirable despite the style being impossible to read and custom operators were a good thing. I personally hate every Haskell code base I have ever seen despite being relatively fluent in the language (an issue Ocaml never had amusingly mostly because its community used to be very pragmatic).
- tome 2y agoSo when you said > I think we agree Haskell is far too dogmatic about its abstractions when it's impractical to be used as a general-purpose language did you mean "sometimes some Haskellers use point-free style and it's impossible to read"? If not, could you explain what you did mean?
- kerkeslager 2y agoThe person you are responding to didn't say that, I did. The abstractions I'm pointing at are cases where mutation or side effects are the desired result of execution. Ultimately this always runs up against having to grok a lot of different monads and that's simply never going to be as easy to understand as calling "print" or "break". Haskell works really well if the problems you're solving don't have a ton of weird edge cases, but often reality doesn't work like that. The other thing is laziness which makes it hard to reason about performance. Note that I didn't say it's hard to reason about execution order--I think they did a good job of handling that. Don't get me wrong, Haskell's dogmatic commitment to functional purity has led to the discovery of some powerful abstractions that have trickled out into other languages. That's extremely valuable work.
- tome 2y ago> The person you are responding to didn't say that, I did. Ah, thanks, I got confused. > Haskell works really well if the problems you're solving don't have a ton of weird edge cases, but often reality doesn't work like that. In my experience it's completely the opposite, actually. I can only really write code that correctly handles a ton of weird edge cases in Haskell. It seems that many people think that Haskell is supposedly a language for "making easy code elegant". The benefit of Haskell is not elegance or style (although it can be elegant). The benefit is that it makes gnarly problems tractable! My experience trying to handle a ton of weird edge cases in Python is that it's really difficult, firstly because you can't model many edge cases properly at all because it doesn't have sum types and secondly because it doesn't have type checking. (As I understand it they have added both of these features since I last used Python, but I suspect they're not as ergonomic as in Haskell.) > this always runs up against having to grok a lot of different monads and that's simply never going to be as easy to understand as calling "print" or "break" Actually, I would say not really. The largest number of monads you "have to" learn is one, that is, the monad of the effect system you choose. Naturally, not every Haskell codebase uses an effect system, and those codebases can therefore be more complex in that regard, but that's not a problem with Haskell per se, it's an emergent property of how people use Haskell, and therefore doesn't say anything at all about whether Haskell is usable as a general purpose language. For example, consider the following Python code. def main(): for i in range(1, 101): if i > 4: break print(i) You can write it in Bluefin[1], my Haskell effect system as follows. main = runEff $ \ioe -> withJump $ \break -> do for_ [1..100] $ \i -> do when (i > 4) $ do jumpTo break effIO ioe (print i) Granted, that is noisier than the Python, despite being a direct translation. However, the noise is a roughly O(1) cost so in larger code samples it would be less noticeable. The benefit of Haskell here over Python is 1. You don't get weird semantics around mutating the loop variable, and it remaining in scope after loop exit 2. You can "break" through any number of nested loops, not just to the nearest enclosing loop (which is actually more useful when dealing with weird edge cases, not less) 3. You can see exactly what effects are possible in any part of the program (which again is actually more useful when dealing with weird edge cases, not less) Regarding laziness and performance, that is a resolved issue. I have an article that explains that: http://h2.jaguarpaw.co.uk/posts/make-invalid-laziness-unrepresentable/ http://h2.jaguarpaw.co.uk/posts/make-invalid-laziness-unrepr... I'm curious what you think of Haskell suitability for general purpose programming in light of my response. [1] https://hackage.haskell.org/package/bluefin-0.0.6.1/docs/Bluefin.html https://hackage.haskell.org/package/bluefin-0.0.6.1/docs/Blu...
- kerkeslager 2y ago> Granted, that is noisier than the Python, despite being a direct translation. My complaint isn't the noise. My complaint is: can you explain what withJump does? Like, not the intention of it, but what it actually does? This is a rhetorical question--I know what it does--but if you work through the exercise of explaining it as if to a beginner, I think you'll quickly see that this is isn't trivial. > 1. You don't get weird semantics around mutating the loop variable, and it remaining in scope after loop exit Is this an upside? It's certainly unintuitive, but I can't think of a case this has ever caused a problem for me in real code. > 2. You can "break" through any number of nested loops, not just to the nearest enclosing loop (which is actually more useful when dealing with weird edge cases, not less) Again, is this actually a problem? Any high school kid learning Python can figure out how to set a flag to exit a loop. It's not elegant or pretty, but does it actually cause any complexity? Is it actually hard to understand? And lots of languages now have labeled breaks. Arguably the Lua solution (gotos) is the cleaner solution here, but that's not popular. :) > 3. You can see exactly what effects are possible in any part of the program (which again is actually more useful when dealing with weird edge cases, not less) What does this even mean? In concrete terms, why do you think I can't see what effects are possible in Python, and what problems does that cause? In all three of the cases that you mention, I can see a sort of aesthetic beauty to the Haskell solution, which I appreciate. But my clients don't look at my code, they look at the results of running my code. > Regarding laziness and performance, that is a resolved issue. I have an article that explains that: http://h2.jaguarpaw.co.uk/posts/make-invalid-laziness-unrepr http://h2.jaguarpaw.co.uk/posts/make-invalid-laziness-unrepr... The fact that you need a blog post to tell people how to resolve an issue exemplifies my point that this is not resolved. Nobody needs to be told how to turn off laziness in Python, because it's not turned on. The fact is, Haskell does the wrong thing by default here, and even if you write your code to evaluate eagerly, you're going to end up interfacing with libraries where someone didn't do that. Laziness still gets advertised up front as being one of the awesome things about Haskell, and while experience Haskell developers are usually disillusioned with laziness, many Haskell developers well into the intermediate level still write lazy code because they were told early on that it's great, and haven't yet experienced enough pain with it to see the problems.
- 2y ago