Hacker News new | past | comments | ask | show | jobs | submit login

My 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.




I 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.


I 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...


>I consider it very inelegant to sort a list only for extracting the largest element.

Which I explicitly stated would not be happening in my example. In reality, you couldn't just use "sort", you'd need to make sure it was a sort with the right behavior. But the point wasn't to show how to get the largest element in a list but rather to show how non-strict (i.e. "lazy") evaluation can make the simplest code actually correct. Obviously this doesn't apply in every possible case, but if you compare it to e.g. C which is pretty much never the case, it's a win in elegance IMO.

>just because the type system doesn't know that the list is non-empty (which I assume here).

In modern haskell there is a type where the list is statically known to be non-empty.

> noticing that the underlying basics have changed AGAIN in Haskell...

Not sure what you mean here, are you complaining about standard library changes? I personally hope Haskell keeps periodically breaking backwards compatibility until they fix more of the ugly parts of the standard. I'd hate to see it go the way of Java and be stuck with a hideous e.g. file access model because that's what the very first release had.

>Really, I consider the following C thing to be so much clearer and better engineering:

I find such a function not remotely clear and definitely not better engineered.

* It's using a "for loop" so it could be a filter, fold, map or any combination of those. I can't know without reading it.

* The function this proposes to replace was able to find the max of any type which can be ordered. This proposed replacement can only do ints. Given the radically reduced scope, the typing is sufficient but you won't have to add many features before the C compiler simply cannot help you anymore. Haskell's type system is much more powerful, stopping just short of the more advanced dependant type applications.


> Which I explicitly stated would not be happening in my example. In reality, you couldn't just use "sort", you'd need to make sure it was a sort with the right behavior.

I'm pretty sure most experienced software architects would not approve this as a robust approach. Notice that this is a property that actually matters, and it's not captured in the type system (most things aren't).

>>just because the type system doesn't know that the list is non-empty (which I assume here).

>In modern haskell there is a type where the list is statically known to be non-empty.

Sure, go ahead and add another type to make everything more incompatible... no, this approach doesn't work out in practice. There are much more invariants than you can hope to capture with new types in any practical project.

> I personally hope Haskell keeps periodically breaking backwards compatibility until they fix more of the ugly parts of the standard. I'd hate to see it go the way of Java and be stuck with a hideous e.g. file access model because that's what the very first release had.

I absolutely agree with you that everybody should strive to improve their own stuff. But with public, standard libraries, it's a different thing. You simply can't afford to have a hard dependency on stuff that breaks randomly. From an economic perspective, it's much, much simpler to just write your own getMin routine without any dependencies at all. It's not rocket science. And the practical gains by some vague idea of "type safety" for this little procedure are nearly zero.

> I find such a function not remotely clear and definitely not better engineered.

> * It's using a "for loop" so it could be a filter, fold, map or any combination of those. I can't know without reading it.

This is just not true. Almost any beginner to programming can understand this immediately, it's not much effort to understand this to almost anyone. Contrary to the Haskell version (the more realistic one - the one I listed). The average time to ramp up any beginner to understand the Haskell version, and the average time to anyone already competent enough to figure this ad-hoc thing out, is much higher.

Do not think that just because there is a for loop this is somehow wrong. There is a simple statement below the for loop and everyone can immediately spot what happens there (it's a "fold", if you like to think in these terms. That was not hard). Any suggestion to the contrary, that's just zealotry.

> * The function this proposes to replace was able to find the max of any type which can be ordered. This proposed replacement can only do ints. Given the radically reduced scope, the typing is sufficient but you won't have to add many features before the C compiler simply cannot help you anymore. Haskell's type system is much more powerful, stopping just sort of the more advanced dependant type applications.

Sure, thanks, I know that. See, I don't care. I don't need that in my own practice. If I want to find a routine which returns the max integer I do this. If I really needed to write a generic routine (which I don't), I would do C++ or Python or whatever other standard procedural approach. Nothing wrong with that. Any of these is more understandable, more deterministic in runtime behaviour, more modular (less dependencies), easier to write, easier to maintain, etc.


>I'm pretty sure most experienced software architects would not approve this as a robust approach.

What approach? Not caring which sort is used? I explicitly stated that in actual practice I wouldn't do that. I simply took a quick example to make a point and you're turning it into an interigation. Look, you don't like Haskell. Fine, don't use it. I'm not cashing checks from your choice of programming language, use whatever you like.


No, I actually mean the approach where you explicitly pick a "sort" implementation that would yield a linear-time smallest-element when evaluated lazily. I don't know if a popular sort with this property exists, but anyway, relying on that is too clever. It's not obvious and also fragile (maybe the sort changes at some point its constant or even asymptotic behaviour?)

If you want the smallest element, get the smallest element by scanning the list. No big deal.

Well, I'm sorry for turning this into almost an emotional issue. I guess I am the one who is cashing checks here. It does annoy me: I have seen way too many advocating that makes it seem like this vague idea of "safety by type-safety" is the only issue that comes up in software development. It is not. It doesn't help all that much for correctness, and other aspects like modularity, efficiency, portability, compiling speed, development speed etc. are at least equally important for the vast majority of projects.

(I could say I've "wasted" a lot of time for investing in Haskell before learning that these other qualities matter as well, but I would not go this far. It's been a good investment, even if it's not a practical language)

It is no coincidence that most Haskell projects never take off. Just look around at the software in existence. What you can find is mostly little tinker projects. When I look at some higher-profile libraries for example for web programming, they focus way too much on some perceived gains from very advanced type system tricks, but they are basically too hard to use in a practical setting. The big links page on Haskell.org has mostly toy projects and broken links. When I look at the open projects page at toptal.com I can see exactly one Haskell project, and it is about refactoring a completely unmaintainable Haskell codebase!

There are areas, like compilers, where Haskell seems to do quite well, but for the most part it is impractical. Even most Haskellers don't disagree that it's mostly good for expanding your mind, exploring mathematical ideas etc.


> I actually mean the approach where you explicitly pick a "sort" implementation that would yield a linear-time smallest-element when evaluated lazily

Agreed. It's quite painful to see Haskell advocates sharing this example. It's far too clever and lacks robustness. It's a cute triviality, not an example of what the language is actually good for.

> I don't know if a popular sort with this property exists

Well yes, Haskell's sort function has this property. It's a merge sort.

> If you want the smallest element, get the smallest element by scanning the list. No big deal.

Yes, or just use the built in min function.

> I have seen way too many advocating that makes it seem like this vague idea of "safety by type-safety" is the only issue that comes up in software development.

It's a shame you seen too many advocating that, because that's not all that Haskell's good for. It's also good for

> modularity, efficiency, portability, ..., development speed

(not "compiling speed"! :) )


>It's not obvious and also fragile (maybe the sort changes at some point its constant or even asymptotic behaviour?)

Tome pointed out that "sort" in haskell is a merge sort, but I have to say I strongly disagree with this point. I was a hardcore C++ proponent for a long time and the reason for that was the STL. What made the STL great, for me, was that the performance was effectively part of the spec of the container types.

So it's true, if you wanted to implement "max" as I did in my example you couldn't use a sort that was simply "sort as efficiently as possible" or some such. You would need a function known to be "mergeSort" or similar. I don't view this as fragile, it's higher level programming: building upon blocks with known correct behavior. It is not trivial either, but I'm not sure engineering can be.

>Well, I'm sorry for turning this into almost an emotional issue.

If you're arguing in good faith, then all is well. There are just a hand full of trolls out there that have to go out and trash haskell where ever they see it mentioned, so if the conversation develops that kind of feel I tend to back out.

>I have seen way too many advocating that makes it seem like this vague idea of "safety by type-safety" is the only issue that comes up in software development.

For me, the reason I switched to Haskell was simply time: I need as much help from the system as I can get. The more the system can find bugs for me the better. But, of course, this only works if you go "all in". You need to make an effort to push as much information as you can into the type system where the compiler can help you (I find this part similar to how I programmed C++). You need to make an effort to make invalid programs impossible to compile. If one doesn't do these things then they probably won't get enough out of the language to make it worth it.

>It doesn't help all that much for correctness, and other aspects like modularity, efficiency, portability, compiling speed, development speed etc. are at least equally important for the vast majority of projects.

For correctness, it depends very much on how successful one is in encoding their program into the type system.

I find modularity to be good but tends to be a community problem more than a language problem: for what ever bizarre reason, Haskell programers tend to not like imports and/or dependancies. Amusingly, one of the most popular packages in Haskell (Lens) mostly ignores these convensions.

Efficiency can be done but it's effort. I find Haskell quite good here: you can do the simpliest thing and it will often be good or correct. If benchmarking tells you that it's too slow then you have a lot of opportunity to make things a lot more efficient without losing all of the elegance.

Portability, I find also good. Not the most portable language that exists but workable.

Compiling speed... I don't see Haskell ever competing with languages like e.g. Go on compile speed. Ocaml is pretty fast though. :)

Development speed, IMO, depends on having a good IDE. Some members are working on this but Haskell doesn't have the community of more popular languages to get this one completed and polished. I would expect Haskell development with an IDE on the level of Visual Studio+Resharper to be the fastest of typed languages because e.g. refactoring would have so much better information to work with (assuming proper use of the type system).

>It is no coincidence that most Haskell projects never take off.

Well, I doubt that is a coincidence but I suspect there are a lot of reasons. Haskell doesn't have the popularity of e.g. Java so it won't have the amount of libraries, the robustness, etc. For me, Java is a classic example of what community focus can do: the language is trivial. It's not powerful at all, all it has are classes, functions and interfaces basically, yet so many people have been working with it for so long it has libraries and software for most anything one can think to do with it. There are tools to deal with its horrible verbosity, etc.

For me, I have no expectation of Haskell to compete directly with Java. My only expection is that Haskell would get to a state at least as good as Java with a lot smaller community (though, obviously, larger than it currently has).

>they focus way too much on some perceived gains from very advanced type system tricks, but they are basically too hard to use in a practical setting.

I'm not sure on this. The type system for e.g. Servant is so good you can impliment trivial servers basically with the types alone. Of course, the critical question is: is this the place most people are having difficulty in web development? If it's not then this effort, cool as it is, will obviously have a limited effect on anything.

>When I look at the open projects page at toptal.com I can see exactly one Haskell project,...

Is this not likely to be the consequence of community size? Community size probably has some relation to language but, for me, it's hard to nail down. Haskell works different than other languages so that, on its own, will exclude a lot of people no matter how good it is. Java took C++'s well known syntax and get rid of anything remotely difficult to use, so that's easy for popularity.

>Even most Haskellers don't disagree that it's mostly good for expanding your mind, exploring mathematical ideas etc.

This could be, I don't know what most Haskellers want with it. I know there are companies that use it in production and I use it as a productive language.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: