12

(unimportant background info / motivation)

I was implementing a different version of nub, inspired by the Yesod book's discouragement of using it.

map head . group . sort is more efficient than a call to nub. However, in our case, order is important...

So I set out writing a "better" nub akin to the order-unimportant version. And I ended up with this:

mynub = unsort . map head . groupBy (\x y -> fst x == fst y) . sortBy (comparing fst) . rememberPosition

rememberPosition = flip zip [0..]
unsort = map fst . sortBy (comparing snd)

This certainly does a lot of extra work, but it should be O(n log n) instead of original nub's O(n2). But that's beside the point. The problem is, it's so long! It's really not that complicated, but it's long (and I'm one of those people that hates going wider than 80 columns, or horizontal scrollbars on StackOverflow code blocks).

(the question)

What are better ways in Haskell for expressing long chains of function composition such as this?

Dan Burton
  • 53,238
  • 27
  • 117
  • 198
  • 4
    Three important things to note. `nub` just has an `Eq a` constraint, and your version has an `Ord a` constraint. `nub` works on infinite lists, your version doesn't. Also, `nub`'s worst-case may be worse than your code, but its best case is better than your code. The most significant difference is the `Ord a` constraint. If you allow that, you can write something more complicated that is O(n log n) worst-case, almost as good as `nub` in the best case, and works on infinite lists. But it involves non-list data structures. – Carl Apr 20 '11 at 18:02

2 Answers2

17

Break up the line, and use the layout:

mynub = unsort 
      . map head 
      . groupBy ((==) `on` fst)
      . sortBy (comparing fst) 
      . rememberPosition
Don Stewart
  • 137,316
  • 36
  • 365
  • 468
8

line width is easily solved :)

> mynub = { unsort 
>         . map head 
>         . groupBy (\x y -> fst x == fst y) 
>         . sortBy (comparing fst) 
>         . rememberPosition
>         }

but I'm barely used to reading composition right to left. Top to bottom is a bit much. Arrow or (>>>)=flip (.) looks nicer to me, but I have no idea if it would be idiomatic

> mynub = { rememberPosition
>       >>> sortBy (comparing fst) 
>       >>> groupBy (\x y -> fst x == fst y) 
>       >>> map head 
>       >>> unsort 
>         }
llayland
  • 203
  • 1
  • 5
  • 1
    `(>>>)` is somewhat unidiomatic, but is defined in the `base` library: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Category.html#v:-62--62--62- – Don Stewart Apr 20 '11 at 04:19