5 ms·
Although Haskell has a relatively powerful and useful type system, you are perhaps being a little kind to it here. Haskell also has partial functions, including
by Silhouette 9y ago
Although Haskell has a relatively powerful and useful type system, you are perhaps being a little kind to it here. Haskell also has partial functions, including quite a few dangerous examples in the standard library, various mechanisms to escape the type system entirely, and 27 different ways to handle errors, 28 of which look a bit like exceptions but need handling in 29 different ways. And it still can't handle basic record types very elegantly, nor dependent types such as fixed-size arrays/matrices. Haskell's type system does have a lot going for it and it also makes a useful laboratory for experiments with type-based techniques, but as far as safety goes it has never really lived up to the hype, and for all its advanced magic, it is surprisingly lacking in support for some basic features that are widely available in other languages.
- hardwaresofton 9y agoYou are absolutely right -- there are other ML-family languages that do a better job at avoiding some of the pitfalls (but surely with other tradeoffs). Most of the stuff you've mentioned is actively recognized by the Haskell community and has been somewhat improved: > Haskell also has partial functions, including quite a few dangerous examples in the standard library, Partial functions -> https://wiki.haskell.org/Avoiding_partial_functions https://wiki.haskell.org/Avoiding_partial_functions (introduces the safe library @ https://hackage.haskell.org/package/safe https://hackage.haskell.org/package/safe) > various mechanisms to escape the type system entirely Type systems are leaky abstractions. While that's just a random assertion with no fact to back it up, I think it is a good idea design wise to account for that possibility, and give people the ability to completely bin the type system when they need to. Most of that stuff is marked with some wording that strongly suggests against use, but does not prohibit (e.g. unsafePerformIO) > 27 different ways to handle errors Could you elaborate? I understand this is hyperbole, but I feel like the number in actuality is <= 3... which might make your statement ~90% hyperbole, which is a bit much. > still can't handle basic record types very elegantly Compared to which language? Records in Haskell are indeed a PITA to get values out of when they're deeply nested, and you have to destructure or string to gether the accessor functions or something, but I still like it more than other languages which take a looser approach, often leading to runtime errors. If I do a obj.something.somethingElse, that line might blow up at runtime, but in Haskell I have some guarantees/checking that it won't. C#'s `?.` operator is pretty good for accessors when dealing with nullable values as well. The lens library helps with that, but though it's simple in function, the mathematics around it that are often introduced at the same time complicate things. > nor dependent types https://www.reddit.com/r/haskell/comments/8cp2zg/whats_the_status_on_dependent_types_in_ghc_as_of/ https://www.reddit.com/r/haskell/comments/8cp2zg/whats_the_s... > laboratory for experiments with type-based techniques Haskell is less of a laboratory these days, people are actually using it for production stuff now, and they can't change core pieces of the languages so quickly -- I'm sure people would love to go back and eliminate partial functions from the standard library where reasonable. Languages like Ada might be better for experimental type stuff? > never really lived up to the hype Compared to what? Would you consider Haskell better for type safety than say Java/C#? Because I certainly do (I'd love to be proven wrong as well). I assume you meant the features you noted above as missing (easy record types, coherent error handling, lack of partial functions) in Haskell but widely available in other languages -- could you note which ones you had in mind? Java is what I've seen used most in enterprise and every function is basically a huge walking partial function -- Haskell's type system (at the very least) seems like a huge step up there. Things got better with 1.8 and the introduction of Optional (and are probably even better these days) but back when I wrote a lot of Java, it didn't seem right to 'poison' all the code with Optional<>s everywhere... Now I know better, I wasn't poisioning the code, I was writing better code but couldn't recognize it at the time. Contrast this with Java that waited until 1.8 for functions as a first class object.
- gizmo686 9y ago>> 27 different ways to handle errors > Could you elaborate? I understand this is hyperbole, but I feel like the number in actuality is <= 3 Not quite 27, but exceptions in Haskell are quite messy. You have: 1) Exceptions. Which can be thrown from pure code, but only caught in IO. And which get thrown when evaluated, which causes a mess with Haskell's lazy evaluation. So the following code is not safe: val <- catch (willThrowError) handler print val Because willThrowError may return a thunk that will not throw an error until you attempt to resolve it to a value. This means that it will not actually throw an error until `print val`, at which point it is outside of the catch, so your entire function will error, and your handler will not be called. To be safe, you want to do something like: val <- catch (evaluate . force <$> willThrowError) handler print val Which requires the return value of willThrowError to be a support type. With the proper language extensions, it is not difficult to autogenerate the instance needed for a type to work with force, so long as you control the type definition and all the types it relies on supports it. I avoid actual Exceptions like the plauge, so I might be off on some details here. 2) Maybe/MaybeT/Either/EitherT Arguably 4 methods, but I will concede that they are the same-ish. 3) ExceptT. Maybe belongs in catagory 2? 4) MonadError 5) MonadFail I might be missing somne.
- hardwaresofton 9y agoHey thanks for clarifying -- Maybe I've just been a victim too long, but those actually don't seem like distinct ways. As far as why you can throw an exception in pure code but only catch it in monad, much ink has been spilled[0]. I won't claim to completely understand the reasoning in that SO post and the linked paper[1], but the reasons for this dichotomy seem to be clear. 2) -> 5) are not ways of handling errors though, they're ways of characterizing/combining monadic computations that CAN error, IMO. In the end, it's still the errors-as-values (`Either SomeException value`) concept, but in combined with the fact that your computation is in a monadic context. Also, maybe I've just been brainwashed by myself to think that way after doing Haskell for this short amount of time. In practice, how I throw an error from a monadic computation (I generally have never done it from pure code) has been pretty well-defined, I haven't had to go down this particular rabit hole much at all -- ExceptT is what I've used the most (I've done most of my work with Servant[2]). Honestly what I've had problems the most with from my haskell code was dealing with the database or mail servers and tcp sockets and stuff -- stuff that failed at runtime because I put in some bad code Haskell couldn't check before runtime. That said, monads & monad transformers are indeed a complex concept, and are very confusing to beginners (I'm still not 100% confident in my knowledge of them), so this is definitely a point against Haskell. And of course your earlier point about unsafe functions in the std lib is as valid as ever. [0]: https://stackoverflow.com/questions/12335245/why-is-catching-an-exception-non-pure-but-throwing-an-exception-is-pure#12345665 https://stackoverflow.com/questions/12335245/why-is-catching... [1]: https://www.microsoft.com/en-us/research/publication/tackling-awkward-squad-monadic-inputoutput-concurrency-exceptions-foreign-language-calls-haskell/?from=http%3A%2F%2Fresearch.microsoft.com%2Fen-us%2Fum%2Fpeople%2Fsimonpj%2Fpapers%2Fmarktoberdorf%2F https://www.microsoft.com/en-us/research/publication/tacklin... [2]: https://haskell-servant.readthedocs.io/ https://haskell-servant.readthedocs.io/