5 ms·
I agree that Haskell is made more difficult than necessary by sticking to purity so tightly [0] Having said that, Haskell is very much not built around the que
by gizmo686 9y ago
I agree that Haskell is made more difficult than necessary by sticking to purity so tightly [0]
Having said that, Haskell is very much not built around the question "what if everything was a monad". Monadic IO wasn't even a thing until Haskell 1.3
https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/history.pdf?from=http%3A%2F%2Fresearch.microsoft.com%2Fen-us%2Fum%2Fpeople%2Fsimonpj%2Fpapers%2Fhistory-of-haskell%2Fhistory.pdf https://www.microsoft.com/en-us/research/wp-content/uploads/...
[0] Although this is, in large part, forced by its unfortunate choice of being lazy. It has also, arguably, forced advances in PL research that would have been avoided had Haskell added first class support for imperative programming.
- jstimpfle 9y agoI agree with you, and disagree with my quote! There are various of these types of lists on the web and I just picked the first I could find, arguably not the best.
- aninhumer 9y agoIndeed. I would argue that a better line for Haskell would be "What if everything was a value?" Basically the IO monad is a way to construct imperative programs by composing side-effect free values. And indeed, much of the subsequent work in Haskell has been materialising even more details to avoid the hidden side-effects that cause the IO monad abstraction to break down.
- alien_at_work 9y ago>forced by its unfortunate choice of being lazy Being lazy is what makes the language so elegant. Even a high level game developer (Carmack?) said, at one point, that his ideal language would be lazy by default.
- aninhumer 9y agoMy impression is that laziness forced people to invent new abstractions to deal with IO, because you can't rely on the easily predictable evaluation order of eager evaluation to provide a good enough solution. And it's those abstractions that make Haskell elegant. However now that we've invented them, while some things benefit from laziness, eager evaluation is probably a better default.
- alien_at_work 9y agoI would disagree with your impression. Laziness made lots of things very elegant but things like IO initially very hard. Eventually elegant solutions were found for IO as well. An example of the elegance is a simple function that takes some type which can be ordered and picking the "largest" one: max :: Ord a => [a] -> a max = head . sort (<) The type declaration says the function "max" takes a list of some type a, which is Orderable and will return one. The implementation says first sort the list, then return the first entry of the sorted list. In a strict language this would be a lot of wasted effort but in a lazy language (depending on how "sort" is implemented) what ends up happening is basically the same steps you would have to do to pick a max value. The unnecassary sorting of the rest of the list cannot happen because that result is thrown away before it can be accessed. If you asked someone to describe how to get the largest entry in a list, the most consice way to describe it would be to sort the list and then take the first entry. In Haskell the most concise way to describe something is often the correct implementation as well, and that's what I call elegance.
- jstimpfle 9y agoI consider it very inelegant to sort a list only for extracting the largest element. Usually sorting refers to a O(n log n) operation, which is a waste. Yes, for some sorting implementation taking the first element of the sorted list may actually amount to a linear time operation given that the sorting is carried out lazily, but that's not very obvious and may fail on you fast. Bad idea from an engineering standpoint. There is a better solution in Haskell, which is getMax $ mconcat (map Max [1,2,3,0::Int]) Or more abstractly findMin :: (Ord a, Bounded a) => [a] -> a findMin = getMax $ mconcat . map Max You need the ::Int so that it's bounded (by the smallest representable value, which Int does have, but Integer does not have). Now, if you actually try getting this version to work, you will notice there is a lot of conceptual overhead involved for such a small thing. And it bugs me that you have to use the "Bounds" machinery just because the type system doesn't know that the list is non-empty (which I assume here). So I ended up with it after fiddling with foldr1, mappend and so on, noticing that the underlying basics have changed AGAIN in Haskell... Really, I consider the following C thing to be so much clearer and better engineering: int getMin(int *a, int n) { int r = a[0]; for (int i = 0; i < n; i++) if (r > a[i]) r = a[i]; return r; } Apart from the fact that in practice there is usually enough context that you will never need such a function, because you get the minimum in constant time with some other clever trick...