News for hackers. Stuff that matters.
Re:Trying Haskell by Peaker on Sunday April 10, 2011 @12:03PM (#2429706)
Haskell's type system can also express interesting things most languages' type systems can't.

For example:

https://github.com/yairchu/red-black-tree/blob/master/AvlTre... -- in lines 13..21, an AVLTree type is defined -- with its invariants encoded in the type system. If there's a mistake in these 9 lines, you may get a wrong program. But the nice thing is that if you get just these 9 lines right -- then the hundreds of lines below it that implement an AVL tree cannot get the AVL invariants wrong.

The same is also true for Red Black Trees: https://github.com/yairchu/red-black-tree/blob/master/RedBla...

Lines 26..36 inclusive encode the RBTree type such that the invariants are enforced by the type-checker.

In case this is unclear: the type-checker enforcing the correctness of the invariants is at compile-time. A running program is a correct program, at least from the invariants' perspective.

Re:Trying Haskell by jbapple on Sunday April 10, 2011 @05:19PM (#2430437)
GADT are really cool, but I would like to note three caveats:

1.

> in lines 13..21, an AVLTree type is defined -- with its invariants encoded in the type system

Its balance invariants are encoded, but its ordering invariants are not.

2. The code for manipulating AVL and red-black trees in GADT form is significantly more complicated that it would be if they were not in GADT form.

3. Sometimes, whether using GADTs or not, one ends up with code like this, from the AVL example:

> Nil -> undefined -- shouldnt happen

Needless to say, this negatively affects the comfort we might receive from the type system.

Having said all that, I think there are cases where GADTs are really worth the extra effort. Sometimes, they even simplify code when the alternative to GADTs is checking things at run-time!

Re:Trying Haskell by Peaker on Tuesday April 12, 2011 @12:46PM (#2437965)
1. True, this is harder in Haskell. More easily possible in a language like Agda.

2. The assurances you get in return are probably worth it.

3. I think that piece is unnecessary, and the type system will see that constructor is impossible. If it doesn't, it's a limitation of Haskell GADT's, and can be fixed. I don't think you ever resort to "Nil -> undefined" in Agda.

Re:Trying Haskell by kd0amg on Sunday April 10, 2011 @04:54PM (#2430387)
How much does this much use of the type system's invariant enforcement increase compilation time? (still just getting into Haskell)
Re:Trying Haskell by Zaak on Sunday April 10, 2011 @05:34PM (#2430472)
That's a very interesting question. I made a stab at removing the invariant enforcement from the AVL tree code, but it was more involved than I expected.

In this specific case, it doesn't matter much since compilation takes less than a second on my underpowered netbook. I'm curious whether it's an issue for a program of significant size.

Re:Trying Haskell by billmcneale on Sunday April 10, 2011 @12:48PM (#2429818)
This might be beautiful code but come on, not a single comment?
Re:Trying Haskell by silentbicycle on Sunday April 10, 2011 @04:37PM (#2430342)
See those type declarations? Those are comments. Comments that are automatically checked.
Re:Trying Haskell by Zaak on Sunday April 10, 2011 @05:39PM (#2430483)
On the one hand, I agree. On the other hand, a type declaration cannot possibly tell you /why/ unless the type system encodes a significant portion of our physical world.

This encoding is left as an exercise for the reader.

Re:Trying Haskell by silentbicycle on Sunday April 10, 2011 @07:29PM (#2430715)
Agreed, generally. I'd say comments are to express things the code itself cannot. More expressive languages can get by with good naming, some declarations, and clear idioms in many cases.

The most useful kind of comments are "why" comments, about why specific design trade-offs were chosen.

Re:Trying Haskell by billmcneale on Sunday April 10, 2011 @07:25PM (#2430704)
<sigh> So you're one of these "Code properly written shouldn't need comments" people, uh?

To understand code, you need to figure out "why" and "what". Code gives you one, comments give you the other.

Re:Trying Haskell by silentbicycle on Sunday April 10, 2011 @07:38PM (#2430730)
I mainly agree with you, but for something like that, "it's an AVL tree, look it up" is all it should need. Names and type declarations handle the rest.

If you want to dismiss me as "one of those" people that is anti-commenting, you're reading too much into what I said. You said, "it doesn't have comments!", I said "actually, it does, and they're even checked." The end.

In a more complex program, I'd add comments. How many, and the nature of the comments, depends on whether I was writing in something relatively low-level (like C) or something high-level (like Haskell, Erlang, or K).

Re:Trying Haskell by Peaker on Sunday April 10, 2011 @02:18PM (#2430015)
You might be assuming a little much about the purpose and target audience of this code :-)

I didn't write it, by the way.

Re:Trying Haskell by KirinDave on Sunday April 10, 2011 @01:28PM (#2429912)
Comments: the great deceivers.

To paraphrase: Some people write tricky code and say, "Ah! I will use a comment to make this clear." Now they have two problems.

Re:Trying Haskell by Tiomaidh on Sunday April 10, 2011 @11:14AM (#2429590)
"Oh look, Haskell is great! Why? Static typing and concise code and no side effects!"

I don't disagree on any particular point, but this is hardly news (on HN, at least), and is rather lacking in substance.

Re:Trying Haskell by danieldk on Sunday April 10, 2011 @12:29PM (#2429771)
Indeed. Also, rewriting base library functionality is bad ;):

http://www.haskell.org/ghc/docs/7.0.2/html/libraries/base-4....

Re:Trying Haskell by eru on Sunday April 10, 2011 @12:49PM (#2429823)
Unless it's just an exercise or done for fun.
Re:Trying Haskell by ionfish on Sunday April 10, 2011 @09:12AM (#2429420)
Obligatory comment pointing out that the fabled "quicksort" example in Haskell is not actually quicksort proper since it's not in-place.
Re:Trying Haskell by KirinDave on Sunday April 10, 2011 @01:26PM (#2429904)
Obligatory comment about quicksort being excessively popular because of its catchy name, and all-too-often delivering inferior performance on large datasets due to partial orderings & bad pivot choices.

Obligatory comment about how sorting algorithm speed is far more important when dealing with incredibly large datasets, spanning across multiple machines. Uninsightful (and perhaps glib) point about how Machines Are So Fast These Days that even Bubble Sort appears fast for medium-sized datasets.

Cleverly-worded comment about how Merge Sort is not the sort of sort to let you down in a jam, and how it is particularly suited to distributed computation (where the overhead of allocation is rendered meaningless by the I/O requirements).

Re:Trying Haskell by megrimlock on Sunday April 10, 2011 @03:48PM (#2430239)
Genuine appreciation for concise recapitulation. I felt I could hear the Tivo fast-forward beep-boop sound while reading your comment.
Re:Trying Haskell by chris_j on Sunday April 10, 2011 @11:08AM (#2429582)
Is it actually possible to implement quicksort in a pure functional language like Haskell (with immutable data structures)? Sorting in place would seem to involve mutating the list every time two values are swapped.
Re:Trying Haskell by danieldk on Sunday April 10, 2011 @12:17PM (#2429737)
Yes. For example, the vector package provides mutable arrays:

http://hackage.haskell.org/package/vector-0.7.0.1

Mutability is attained by using the ST monad. The ST monad uses mutable memory, but since it does not allow other interactions with the outside world, its value can be extracted (unlike the IO monad). When you are done with the modification of the vector, it can be frozen to obtain a pure vector.

The IO monad can also be used, but not if you want to return a pure value.

A good tutorial can be found at:

http://www.haskell.org/haskellwiki/Numeric_Haskell:_A_Vector...

I used mutable vectors in the ST monad in maximum entropy training software, and they are really performant.

Re:Trying Haskell by KirinDave on Sunday April 10, 2011 @01:28PM (#2429911)
What blew my mind about the ST monad is that despite its promise of single-threading, it still allows for recursive division of labor.

I was like, "Wait. Wait. Waaaiiiitt. How does it do that?"

Re:Trying Haskell by Hemospectrum on Sunday April 10, 2011 @12:06PM (#2429714)
Haskell is designed to quarantine side effects, not remove them altogether. There's a special monad for building data structures that are mutable at creation time but appear immutable outside that block of code. In normal circumstances, you'd probably still have to copy the contents of the input into a new array before sorting it, though. So, it would still be less memory efficient on large inputs.
Re:Trying Haskell by jmillikin on Sunday April 10, 2011 @11:57AM (#2429693)
Haskell has mutable data structures; you could easily implement in-place quicksort with vectors, arrays, or pointers.
Re:Trying Haskell by merijnv on Sunday April 10, 2011 @09:32AM (#2429443)
Not to mention that Haskell's sort function is actually quite a bit more complex than the stereotypical "quicksort" example shown.
Re:Trying Haskell by danieldk on Sunday April 10, 2011 @09:43AM (#2429456)
For reference, Data.List.sort in GHC: http://www.haskell.org/ghc/docs/7.0.2/html/libraries/base-4....

It's still short and sweet ;).

Re:Trying Haskell by aristidb on Sunday April 10, 2011 @09:37AM (#2429447)
C++'s std::sort in turn is a lot more complex than the quicksort example shown.
Re:Trying Haskell by CJefferson on Sunday April 10, 2011 @12:40PM (#2429797)
To be fair, that is because it is a carefully tuned combination of 3 sort routines (quicksort, heapsort, insertion sort), and also optimisations to avoid unnecessary checks for pointers reaching the end of arrays where they can be avoided.

I would be impressed if a sort which used the same 3 combined techniques could be faster.

(Of course, there might be better sorts, such as TimSort, but that's a change of algorithm, not language).

Re:Trying Haskell by Peaker on Sunday April 10, 2011 @02:20PM (#2430025)
I think timsort is trying to optimize on comparison counts, because comparisons might call back into Python code, which is expensive. Other sorting algorithms optimize different operations.
Re:Trying Haskell by stianan on Sunday April 10, 2011 @11:34AM (#2429632)
Not only is it not proper quicksort, it's very memory inefficient.
Re:Trying Haskell by eru on Sunday April 10, 2011 @12:49PM (#2429822)
Depends on your GC and compiler.
Re:Trying Haskell by stianan on Sunday April 10, 2011 @03:08PM (#2430139)
I'd argue that it's a good thing to use an algorithm that is efficient in and of itself. (And perhaps a language which invites the use of such an algorithm.)
Re:Trying Haskell by eru on Monday April 11, 2011 @04:41PM (#2434261)
The efficiency of algorithms depends on the language / model of computation you are using.

For an extreme example, the best way to write an efficient matrix multiplication in Fortran is to write a naive matrix multiplication. The compiler will recognize the pattern, and transform it.

Re:Trying Haskell by pdhborges on Sunday April 10, 2011 @02:12PM (#2430001)
C++ quicksort :|

    #include <algorithm>
    #include <iterator>
    #include <functional>
    using namespace std;
 
    template <typename T>
    void sort(T begin, T end) {
        if (begin != end) {
            T middle = partition (begin, end,   bind2nd(less<iterator_traits<T>::value_type>(), *begin));
            sort (begin, middle);
            sort (max(begin + 1, middle), end);
        }
    }
from wikipedia
Re:Trying Haskell by chc on Sunday April 10, 2011 @03:53PM (#2430255)
His version is explicit, sort of a C++ translation of the C algorithm (though still a bit longer than necessary even for C). I think he meant to imply "in simple C++ without STL." The awkwardness of partition() is not quite enough to overcome the LOC savings, but it's making a good effort.

The Haskell version also refrains from importing the equivalent Data.List, which would allow us to define `more` and `less` as simply `partition (< x) xs`.