Note that there are some explanatory texts on larger screens.

plurals
  1. POHaskell, terminal call optimisation and lazy evaluation
    primarykey
    data
    text
    <p>I'm trying to write a <code>findIndexBy</code> which would return the index of an element selected in a list by an ordering function. This function is equivalent to sorting the list and returning the top element, but I want to implement it to be able to process lists without size limit.</p> <pre class="lang-hs prettyprint-override"><code>findIndexBy :: (Ord a) =&gt; (a -&gt; a -&gt; Bool) -&gt; [a] -&gt; Integer findIndexBy f (x:xs) = findIndexBy' xs x 1 0 where findIndexBy' [] _ _ i = i findIndexBy' (x:xs) y xi yi = if f x y then findIndexBy' xs x (xi + 1) xi else findIndexBy' xs y (xi + 1) yi </code></pre> <p>With this implementation, I get a <code>Stack space overflow</code> when processing big list, as in the following example (trivial):</p> <pre class="lang-hs prettyprint-override"><code>findIndexBy (&gt;) [1..1000000] </code></pre> <p>I know there should be more elegant solutions to solve this problem, and I'm interested in knowing the most idiomatic and efficient ones, but I really want to understand what is wrong with my function.</p> <p>I might be wrong, but I think my implementation of <code>findIndexBy'</code> is based on terminal recursion, so I don't really understand why the compiler doesn't seem to optimize the tail call.</p> <p>I thought it might be due to the if/then/else and also trying the following, which results in the same error:</p> <pre class="lang-hs prettyprint-override"><code>findIndexBy :: (Ord a) =&gt; (a -&gt; a -&gt; Bool) -&gt; [a] -&gt; Integer findIndexBy f (x:xs) = findIndexBy' xs x 1 0 where findIndexBy' [] _ _ i = i findIndexBy' (x:xs) y xi yi = findIndexBy' xs (if f x y then x else y) (xi + 1) (if f x y then xi else yi) </code></pre> <p>Is there a simple way to ask the compiler to show where tail-call optimization is (not) performed?</p> <p>For reference, below is the equivalent function that I wrote in Clojure, and that I am now trying to port to Haskell:</p> <pre class="lang-lisp prettyprint-override"><code>(defn index-of [keep-func, coll] (loop [i 0 a (first coll) l (rest coll) keep-i i] (if (empty? l) keep-i (let [keep (keep-func a (first l))] (recur (inc i) (if keep a (first l)) (rest l) (if keep keep-i (inc i))))))) </code></pre> <p>For information, the previously quoted Haskell code was compiled using the <code>-O3</code> flag.</p> <p><strong>[edit after leventov answer]</strong></p> <p>The problem seems to be related to lazy evaluation. Although I found about <code>$!</code> and <code>seq</code>, I wonder what is the best practice when using them to fix the original code.</p> <p>I'm still interested with more idiomatic implementations relying on functions from <code>Data.List</code>.</p> <p><strong>[edit]</strong></p> <p>The simplest fix is to add <code>yi `seq`</code> in the first snippet before the <code>if</code> statement.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload