[Haskell-cafe] Basic list exercise

Keith keith.wygant at gmail.com
Sun Mar 26 21:33:50 UTC 2023


YMMV, but based on some benchmarking I've done, the default implementation using `compareBy` seems to optimize better than trying to force dictionary unpacking. The only way I've been able to generically sort faster than base's `compareBy` is to use nonempty list structures for runs.
—
Sent from my phone with K-9 Mail.

On 26 March 2023 19:07:03 UTC, Viktor Dukhovni <ietf-dane at dukhovni.org> wrote:
>On Sun, Mar 26, 2023 at 10:59:13AM -0700, Todd Wilson wrote:
>
>> Is there an overhead penalty to be paid for using functions here, or
>> is it compiled away into Prolog-like tail calls that fill in the
>> missing parts of the data structure?
>
>Efficient implementations of functional langauges make extensive use of
>tail call optimisation.
>
>I don't know whether looking at the GHC-generated "Core" for your
>implementation will answer any of your questions, but here it is:
>
>  Haskell:
>    runs (x:xs) = let (ys, zs) = run x xs in (x:ys) : runs zs
>      where
>        run x [] = ([], [])
>        run x (y:ys) = if x <= y
>                       then let (us, vs) = run y ys
>                            in (y:us, vs)
>                       else ([], y:ys)
>
>  Core (with some added comments):
>    -- Unpack the "Ord" dictionary to extract just the required "<="
>    -- function and call the "$wruns" worker ("ww3" is "<=", and "ds"
>    -- is the list to be transformed:
>    --
>    runs :: forall a. Ord a => [a] -> [[a]]
>    runs
>      = \ (@a) ($dOrd :: Ord a) (ds :: [a]) ->
>          case $dOrd of { C:Ord ww ww1 ww2 ww3 ww4 ww5 ww6 ww7 ->
>          $wruns ww3 ds
>          }
>
>    Rec {
>    $wruns :: forall {a}. (a -> a -> Bool) -> [a] -> [[a]]
>    $wruns
>      = \ (@a) (ww :: a -> a -> Bool) (ds :: [a]) ->
>          case ds of {
>            [] -> [];           -- runs [] = []
>            : x xs ->           -- runs (x:xs) = let (ys, zs) = run x xs in (x:ys) : runs zs
>              let {
>                ds1 :: ([a], [a])  -- A lazily evaluated thunk for (run x xs)
>                ds1
>                  = letrec {
>                      -- Internal recursion in "run" returns strict unboxed pairs
>                      -- (on the stack) avoiding heap or thunk allocation for the tuple.
>                      $wrun :: a -> [a] -> (# [a], [a] #)
>                      $wrun
>                        = \ (x1 :: a) (ds2 :: [a]) ->
>                            case ds2 of wild1 {         -- (y:ys) is "wild1"
>                              [] -> (# [], [] #);       -- run x [] = ([], [])
>                              : y ys ->
>                                case ww x1 y of {       -- x <= y ?
>                                  False -> (# [], wild1 #);  -- else ([], y:ys)
>                                  True ->                    -- then let (us, vs) = run y ys in (y:us, vs)
>                                    let {
>                                      ds3 :: ([a], [a])      -- A "thunk" for (run y ys) evaluated lazily
>                                      ds3 = case $wrun y ys of { (# ww1, ww2 #) -> (ww1, ww2) } } in
>                                    (# : y (case ds3 of { (us, vs) -> us }),
>                                       case ds3 of { (us, vs) -> vs } #)
>                                }
>                            }; } in
>                    case $wrun x xs of { (# ww1, ww2 #) -> (ww1, ww2) } } in
>              : (: x (case ds1 of { (ys, zs) -> ys }))
>                (case ds1 of { (ys, zs) -> $wruns ww zs })
>          }
>    end Rec }
>
>So for a non-empty cons-cell (: x ...) the result is a new cons cell:
>
>    : (x : (fst ds1)) (runs (snd ds1))
>           ---------   --------------
>
>in which both underline parts are computed lazily (on demand) from the
>thunk "ds1":
>
>    λ> head $ head $ runs $ 42 : undefined
>    42
>
>When we do want the successor of the first element, we look no futher
>than necessary:
>
>    λ> head $ runs $ 42 : 0 : undefined
>    [42]
>
>    λ> take 2 $ head $ runs $ 42 : 43 : undefined
>    [42,43]
>
>Does this help?  FWIW, the (simplified) Core output was generated via:
>
>    hscore() {
>        ghc -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes "$@"
>    }
>    hscore -O2 Runs.hs > Runs.core
>
>-- 
>    Viktor.
>_______________________________________________
>Haskell-Cafe mailing list
>To (un)subscribe, modify options or view archives go to:
>http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>Only members subscribed via the mailman list are allowed to post.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20230326/814ab939/attachment.html>


More information about the Haskell-Cafe mailing list