4 ms·
My main problem with Haskell was not the pure, funcational part. It was the side-effect-laden part. If I were ever to go there again, I would probably give OCa
by krylon 9y ago
My main problem with Haskell was not the pure, funcational part. It was the side-effect-laden part.
If I were ever to go there again, I would probably give OCaml a try. It seems to offer many of the benefits of Haskell, while also offering "a way out" into the familiar world of objects and side effects. But don't hold your breath, I don't think I will get there this year.
- hardwaresofton 9y agoWhat do you mean by the "side-effect-laden" part? Did you find yourself writing lots of side effecting code because of Haskell (which seems weird)? Or Haskell just wasn't a good fit for a project that was full of side-effecting code? If it's the latter, I can definitely tell you that part of the zen of Haskell is firming up the boundaries between side-effecting code and pure code -- for example, IMHO the better you get at Haskell, the less `do` statements show up in your code (for better or for worse, because >>=, <*>, and co make it harder for newcomers to enter a codebase)
- krylon 9y agoWriting a purely functional function like, say, factorial, or the Ackermann function, was no problem at all. Writing a "purely" side effect-based one, say, read a string from stdin, parse an age, and say, "What, you are %d years old?!?! Wow, you're old!" completely eluded me. Maybe it was just that all the tutorials I encountered sucked. Maybe I am just too dumb for Haskell. Given that I currently really love Go, I kind of suspect the latter. But who knows? I'll find out next year, I guess. ;-)
- hardwaresofton 9y agoSo yeah, I definitely get that. I'm no Haskell master, but I found that my code progressed this way (assuming I was trying to do what you mentioned": main :: IO () main = do putStrLn "Please input your age:" age <- getLine when (age < 10) (putStrLn "..." ++ (show age)) when (10 =< age < 40) (putStrLn "..." ++ (show age)) when (age >= 40) (putStrLn "..." ++ (show age)) But as you get more comfortable, you get something like: type Age = Int -- Imagine there are constants here for youngAgeMessage, middleAgeMessage, and oldAgeMessage, -- for simplicity they're just strings, though you could add even more descriptive aliases -- like "OldMessage" with a quick union type like AgeMesssage = YoungAgeMessage | MiddleAgeMessage | OldAgeMessage, etc etc makeAgeMessage :: Age -> String makeAgeMessage age | age < 10 = youngAgeMessage ++ show age | 10 =< age < 40 = middleAgeMessage ++ show age | age >= 40 = oldAgeMessage ++ show age main :: IO () main = putStrLn "Please enter your age:" >> getLine >>= putStrLn . makeAgeMessage . read This isn't even the final form of this code either, there are some more things you could do to make this code more axiomatic haskell. This code is approximate (like you probably can't copy and paste it, probably won't compile) but should at least show what I mean. It's similar to early clojure and the use (and eventual love, usually, of the threading macro "->") and the unix philosophy -- once you start writing those clean functions that pass whatever they need right along, it starts getting easier (and more desirable) to pluck out the parts that don't have to be side-effecting. Also, the type system expresiveness is just amazing -- it's what Java should have been but never got the chance to be: data Thing = OneThing | AnotherThing | ThirdThing Guess what a `Thing` can be? literally just those things, not even a null or anything. A lot of people say they really love/need/only use "strongly typed" languages (which means a ton of things to a ton of people), but the biggest slap in the face to me is how a language like Java (that people will reach for when they want to compare some usually weaker language to a "strongly typed" one), is a literal minefield of Null Pointer Exceptions (NPEs). "Anything can be anything or sometimes nil" is a hard pill to swallow once you've used Haskell. Also, trying to use Option<> everywhere feels wrong if you do it in Java, because it starts to bleed everywhere, but actually... it's absolutely right (IMO) -- imagine how much better java code could be if the default was to Option.map over things, and the second you decided to try and pull a value out, you knew you were opening yourself up to something bad, instead of just passing things around and hoping they're there or writing repetitive checks. Now, if you see a `Maybe Thing`, you instantly know that that thing is either JUST the thing, or it's Nothing, and maybe's type is: data Maybe a = Just a | Nothing Type classes are also amazing, they're basically just as ergonomic as Go's interfaces, without some of the weird hangups and interface{} All that said, I definitely get the hangup, Haskell is difficult to get started with, even to this day, but man, that hurdle is so worth. Maybe I'm just addicted to high-learning-curve things but Haskell feels good.
- z1mm32m4n 9y agoThis is a great comment outlining some of Haskell's strengths. Definitely, ADTs should exist in every language claiming their static type system as a feature. Just one thing that stands out to me: > "Anything can be anything or sometimes nil" is a hard pill to swallow once you've used Haskell. In a language like Java yes, nearly every type is inhabited by null. In fairness, a similar problem exists in Haskell: every type is inhabited by "bottom" which is the computation that never terminates. This is a side effect of being lazy by default.
- hardwaresofton 9y agoHey is that truth affected by the talk surrounding enabling strictness with a flag in Haskell? I don't know much about the actual progress/implementation (mostly because I haven't had too many problems with laziness yet) I've heard that Idris is basically Haskell but strict by default -- I've never tried it but maybe I should? I'm hesitant to go to a language with even less libraries/support/mindshare than Haskell though
- Tarean 9y agoI felt the same for a long time. At first glance something like import Text.Printf main :: IO () main = do age <- readLn print (formatAge age) formatAge :: Int -> String formatAge age = printf "What, you are %d years old?!?!?! Wow, you are old" age might look nice and imperative. But then you realize it is just syntactic sugar for `main = readLn >>= printf ...`, which is incredibly confusing when coming from another language. And then you learn that libraries can just invent their own meanings for `>>=` as long as the type signatures match and all bets are off. The upshot of this is that the code like the one above is easy to test and could be async or parallelized without issue. The downside is that people usually fail several times before finally getting it. The haskell book[1] is apparently really helpful with this but it also costs 60$ and is over a thousand pages so it's a pretty large investment in both time and money. [1]: http://haskellbook.com/ http://haskellbook.com/
- krylon 9y ago> The haskell book[1] is apparently really helpful with this but it also costs 60$ and is over a thousand pages so it's a pretty large investment in both time and money. For a really good book on programming, I'll happily pay 60 bucks. I will put that on my Christmas wish list, and give it another try next year.