<div dir="ltr"><div dir="ltr">You can read a recent implementation of sortBy at <a href="https://github.com/ghc/ghc/blob/ghc-8.6.1-release/libraries/base/Data/OldList.hs#L943-L970">https://github.com/ghc/ghc/blob/ghc-8.6.1-release/libraries/base/Data/OldList.hs#L943-L970</a></div><div dir="ltr"><br></div><div>It often helps to work with a concrete example, such as:</div><div><br></div><div><div>head (sort [5, 4, 6, 3, 7, 1])</div></div><div><br></div><div>Expanding that a bit we get:</div><div><br></div><div>head (mergeAll (sequences [5, 4, 6, 3, 7, 1]))</div><div><br></div><div>sequences is linear time, it breaks the list up into monotonically increasing sublists (up to n/2 of them) with pairwise comparisons. It doesn't all get evaluated right away, but nothing "interesting" is happening there so let's show it fully evaluated.</div><div><br></div><div>head (mergeAll [[4, 5], [3, 6], [1, 7]])</div><div><br></div><div>Expanding that we get</div><div><br></div><div>head (mergeAll (mergePairs [[4, 5], [3, 6], [1, 7]])))</div><div>head (mergeAll (merge [4, 5] [3, 6] : [1, 7] : []))</div><div>head (mergeAll (mergePairs (merge [4, 5] [3, 6] : [1, 7] : [])))</div><div>head (mergeAll (merge (merge [4, 5] [3, 6]) [1, 7] : [])))</div><div>head (merge (merge [4, 5] [3, 6]) [1, 7])</div><div>head (merge (3 : merge [4, 5] [6]) [1, 7])</div><div>head (1 : merge (3 : merge [4, 5] [6]) [7])</div><div>1</div><div><br></div><div>This phase is linear time in the worst case, we only compare the first element of each sublist once to find the least element. Having a constant number of phases that are linear (two in this case) is still linear. It would be linearithmic time if we were to fully evaluate the whole sort, but laziness gets to leave a lot of that work unevaluated.</div><div><br></div><div>-bob</div><div><br></div><div><br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr">On Thu, Sep 27, 2018 at 10:16 PM Christian Sternagel <<a href="mailto:c.sternagel@gmail.com">c.sternagel@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">That is interesting. Is anybody aware of a more detailed justification<br>
of how lazy evaluation makes this happen? - chris<br>
<br>
On 09/26/2018 11:38 PM, Bob Ippolito wrote:<br>
> Haskell’s sort algorithm is linear complexity when only evaluating the<br>
> front of the list. See also <br>
> <a href="https://ro-che.info/articles/2016-04-02-descending-sort-haskell" rel="noreferrer" target="_blank">https://ro-che.info/articles/2016-04-02-descending-sort-haskell</a> which<br>
> includes some measurements. <br>
> <br>
> On Wed, Sep 26, 2018 at 10:30 David Feuer <<a href="mailto:david.feuer@gmail.com" target="_blank">david.feuer@gmail.com</a><br>
> <mailto:<a href="mailto:david.feuer@gmail.com" target="_blank">david.feuer@gmail.com</a>>> wrote:<br>
> <br>
>     Laziness does not make the complexity work out fine. Sorting is<br>
>     still O(n log n), which isn't needed here.<br>
> <br>
>     On Wed, Sep 26, 2018, 10:22 AM Tom Ellis<br>
>     <<a href="mailto:tom-lists-haskell-cafe-2017@jaguarpaw.co.uk" target="_blank">tom-lists-haskell-cafe-2017@jaguarpaw.co.uk</a><br>
>     <mailto:<a href="mailto:tom-lists-haskell-cafe-2017@jaguarpaw.co.uk" target="_blank">tom-lists-haskell-cafe-2017@jaguarpaw.co.uk</a>>> wrote:<br>
> <br>
>         Hopefully laziness makes the complexity work out fine. <br>
>         Nonetheless I don't<br>
>         like relying on laziness for the correct complexity and it would<br>
>         still be<br>
>         nice to have an explicit version.<br>
> <br>
>         On Wed, Sep 26, 2018 at 10:13:21AM -0400, Brandon Allbery wrote:<br>
>         > Not exactly that, but you can use groupBy fst . sort, then the<br>
>         head of the<br>
>         > result list is your "minimumsBy" result.<br>
>         ><br>
>         > On Wed, Sep 26, 2018 at 6:28 AM Tom Ellis <<br>
>         > <a href="mailto:tom-lists-haskell-cafe-2017@jaguarpaw.co.uk" target="_blank">tom-lists-haskell-cafe-2017@jaguarpaw.co.uk</a><br>
>         <mailto:<a href="mailto:tom-lists-haskell-cafe-2017@jaguarpaw.co.uk" target="_blank">tom-lists-haskell-cafe-2017@jaguarpaw.co.uk</a>>> wrote:<br>
>         > > Data.List.minimumBy :: Foldable t => (a -> a -> Ordering) -><br>
>         t a -> a<br>
>         > ><br>
>         > ><br>
>         > ><br>
>         <a href="https://www.stackage.org/haddock/lts-12.1/base-4.11.1.0/Data-List.html#v:minimumBy" rel="noreferrer" target="_blank">https://www.stackage.org/haddock/lts-12.1/base-4.11.1.0/Data-List.html#v:minimumBy</a><br>
>         > ><br>
>         > > but there are many cases where that's quite unhelpful. <br>
>         Actually what we<br>
>         > > want is more like<br>
>         > ><br>
>         > >     minimumsBy :: ... => (a -> a -> Ordering) -> t a -> [a]<br>
>         > ><br>
>         > > There can be many distinct minimizers.  For example when I<br>
>         want to get the<br>
>         > > collection of the youngest people from [(Age, Person)] I want<br>
>         > ><br>
>         > >     minimumsBy (compare `on` fst) [(12, alice), (15,<br>
>         balaji), (12, cho)]<br>
>         > ><br>
>         > > to return<br>
>         > ><br>
>         > >     [(12, alice), (12, cho)]<br>
>         > ><br>
>         > > Does "minimumsBy" exist somewhere reasonably standard? <br>
>         Hoogle doesn't<br>
>         > > throw<br>
>         > > up anything obvious<br>
>         > ><br>
>         > ><br>
>         > ><br>
>         <a href="https://www.stackage.org/lts-12.1/hoogle?q=%28a+-%3E+a+-%3E+Ordering%29+-%3E+t+a+-%3E+%5Ba%5D" rel="noreferrer" target="_blank">https://www.stackage.org/lts-12.1/hoogle?q=%28a+-%3E+a+-%3E+Ordering%29+-%3E+t+a+-%3E+%5Ba%5D</a><br>
>         _______________________________________________<br>
>         Haskell-Cafe mailing list<br>
>         To (un)subscribe, modify options or view archives go to:<br>
>         <a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
>         Only members subscribed via the mailman list are allowed to post.<br>
> <br>
>     _______________________________________________<br>
>     Haskell-Cafe mailing list<br>
>     To (un)subscribe, modify options or view archives go to:<br>
>     <a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
>     Only members subscribed via the mailman list are allowed to post.<br>
> <br>
> <br>
> <br>
> _______________________________________________<br>
> Haskell-Cafe mailing list<br>
> To (un)subscribe, modify options or view archives go to:<br>
> <a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
> Only members subscribed via the mailman list are allowed to post.<br>
> <br>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
To (un)subscribe, modify options or view archives go to:<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
Only members subscribed via the mailman list are allowed to post.</blockquote></div>