The immediate exercise threw up enough problems to make it worthwhile. The functions sort and sortBy are in Data.List. sortBy takes a comparison function (and sort is just sortBy compare). So, here was my direct answer to the exercise:

re type signatures: I knew that longer would have to return the same type as compare:

*Main> :type compare
compare :: (Ord a) => a -> a -> Ordering

Decorate-sort-undecorate

That isn’t how I would write this kind of sort in Python (my main language). For a sort with any kind of custom comparison function, I would use the decorate-sort-undecorate (dsu) algorithm. Here’s a verbose implementation:

Passing the comparison function to sort() directly will result in it being called every time two elements from the list are compared. With dsu, the comparison function is called once for each item on the list, then sort() can just use less than.

I must confess that I didn’t work out all those type signatures all by myself. I used Haskell’s type inference to work it out for me. Write the function without a type signature, then ask ghci what type it is:

Remember there’s no difference between a, b and t. They are not types of types, just more or less random letters used by different parts of ghci’s type inference mechanism.

Note that functions seem to be of type (a -> b).

Further work

I asked about decorate-sort-undecorate on Haskell-Beginners and received very helpful, thorough and friendly reponses. Some of their implementations of dsu used Haskell syntax I haven’t come across yet. I’d like to look into this new syntax further and write it up, but that can be another story for another day.

Thanks for your comments (and nice to have a co-author of RWH visit!).

I should have written about comparing. I’ll make sure to do so in my next post. I might have to draw the line before I get to monads, just so I can get back to the book, and come to monads in their proper place.

Maybe it’s reasonable, but I wonder if it’s a good idea to speak of signs like “(.)”, “($)”, “(>>=)”, “(>=>)” and so on as ‘syntax’. They are just names of functions like “sin” or “(+)”.
I guess the point is obvious, and often enough made — but maybe not often enough? For example, if you don’t like any of these seeming syntax-bits you can replace them with a definion:
f `composedWith` g = f . g
or
f `after` g = f . g
You can’t get tired of the ‘do’ in do notation and declare for ‘pleaseDo’, or replace the ‘=’of definitions with something else. There’s no thing or function to rename, they really are syntax. You can get rid of Haskell’s dots with a definition, but you can’t get rid of one of Matz’s dots, because his dots don’t mean anything.

It is the same with Kleisli composition, in whichever direction, as dons noted,it’s a defined function: f >=> g = \x -> f x >>= g. If you preferred you could write, say: f `fishrightward` g = \x -> f x >>= g. Of course whatever you call the thing, you’ll define it in terms of ‘bind’ (>>=). This is something that you yourself have to define explicitly when you declare a functor List or Maybe or MyAmazingTypeClass to be a monad. But wherever you use it, it’s just another NAME for a definite function, no different from “sin” or “(+)”. Thus in the so-called Maybe monad the function in question is named in the instance declaration:
instance Monad Maybe where
Nothing >>= Just f = Nothing
Just x >>= Nothing = Nothing
Just x >>= Just f = Just f x
or something like that. Those lines isolate a mapping between any pair of Maybe values and another Maybe value, and then put a name to it. This is not syntax.
It’s like “show” or “(==)” which have to be defined when you declare a type to be in the Show or Eq type class. “show” certainly doesn’t seem like syntax!

When you get rid of a few sugary things like “do” notation and the fun and games with infix functions, and the inevitable things like the form of import statements and data declarations and definitions (with “=”) and grouping with parentheses, then the only real syntactical sign in Haskell is the white space between terms, which signifies the application of the function named by the first term to the function or object named by the second. So when we leave aside the decorative bits of sugar the founders gave us (which are frequently brilliant!) we see that in a way Haskell doesn’t have any syntax, or that it’s invisible — it’s white space and the “Western” decision to treat the name on the left as naming the function to be applied to the thing name to its right … plus grouping by parentheses, which is force on us by the linear form of type. Basically, when you type something or other, it’s not because of some witty rule Matz cooked up, dot this dot that curly bracket this square bracket that. What you type is the name of the thing you’re talking about. This is part of the reason why Haskell is so beautiful.

People sometimes say that as an exercise, when you come to the monad business, you should avoid the ‘do’ sugar till you figure out what’s going on with (>>=), (>=>) and company. I wonder if it wouldn’t also be a good exercize to eliminate all non alphabetical signs, replacing “.” with “composedWith”, “>>=” with “boundTo” and so on. …It would be a little exhausting! Maybe I should try it.

The problem specification does not impose a requirement that the list elements be comparable, yet dsu does. Hence dsu can’t compare everything that sortByLength can. And when it can, dsu needlessly compares lists whenever they have the same length. Zip, which maps elements to (,) pairs, should be replaced by zipWith f, where f produces pairs that are compared only on first elements.

sortByLength is completely polymorphic on ‘a’, so you can compare any type of list of lists.
The dsu version further requires that the elements be comparable (Ord a). For example, you couldn’t use it to sort lists of functions by length.