Haskell List complexity - list

sorry if this question has already been asked, I didn't find it. And sorry for my poor english.
I'm learning Haskell and try to use lists.
I wrote a function which transforms a list following a specific pattern, I can't check if it works now, but i think so.
This function is not a tail call function, so I think it will be horrible to compute this function with a big list:
transform :: [Int] -> [Int]
transform list = case list of
(1:0:1:[]) -> [1,1,1,1]
(1:1:[]) -> [1,0,1]
[1] -> [1,1]
(1:0:1:0:s) -> 1:1:1:1: (transform s)
(1:1:0:s) -> 1:0:1: (transform s)
(1:0:s) -> 1:1: (transform s)
(0:s) -> 0: (transform s)
So I thought about another function, which would be "better":
transform = reverse . aux []
where
aux buff (1:0:[1]) = (1:1:1:1:buff)
aux buff (1:[1]) = (1:0:1:buff)
aux buff [1] = (1:1:buff)
aux buff (1:0:1:0:s) = aux (1:1:1:1:buff) s
aux buff (1:1:0:s) = aux (1:0:1:buff) s
aux buff (1:0:s) = aux (1:1:buff) s
aux buff (0:s) = aux (0:buff) s
The problem is that I don't know how it compiles and if I'm wrong with the second function. Can you explain me how lists work ? Is it better to use (++) or reverse the list at the end ?
Thank you in advance for your answers

The first function is perfectly fine and in my opinion preferable to the second one.
The reason is laziness. If you have a transform someList expression in your code, the resulting list will not be evaluated unless you demand it. In particular the list will be evaluated only as far as it is needed; print $ take 10 $ transform xs will do less work than print $ take 20 $ transform xs.
In a strict language transform would indeed encumber the stack, since it would have to evaluate the whole list (in a non-tail recursive way) before returning anything of use. In Haskell transform (0:xs) evaluates to 0 : transform xs, a usable partial result. We can inspect the head of this result without touching the tail. There is no danger of stack overflow either: at any time there is at most a single unevaluated thunk (like transform xs in the previous example) in the tail of the list. If you demand more elements, the thunk will be just pushed further back, and the stack frame of the previous thunk can be garbage collected.
If we always fully evaluate the list then the performance of the two functions should be similar, or even then the lazy version could be somewhat faster because of the lack of reversing or the extra ++-s. So, by switching to the second function we lose laziness and gain no extra performance.

Your first version looks much better to me1. It's fine that it's not tail-recursive: you don't want it to be tail-recursive, you want it to be lazy. The second version can't produce even a single element without processing the entire input list, because in order to reverse the result of aux, the entirety of aux must be known. However,
take 10 . transform $ cycle [1,0,0,1,1,1]
would work fine with your first definition of transform, because you only consume as much of the list as you need in order to make a decision.
1 But note that (1:0:1:[]) is just [1,0,1].

Related

Can someone explain OCaml Tail Recursion Like I am five?

I cant wrap my brain around tail recursion specifically in ocaml also explain why one calls the "in" function at the end. P.S. I am talking most basic tail recursive function.
Functions communicate via one of three methods.
Their argument(s). This is the input data.
Their return value. This is the output.
They can modify some mutable state outside of the function's scope. Don't do this if you can possibly avoid it.
Everytime a function is called, a stack frame is created with the data the function needs to do its job. When it's done its return value is returned "up" the stack to its caller.
Consider a simple sum function which sums up a list.
let rec sum lst =
match lst with
| [] -> 0
| x::xs -> x + sum xs
Pretty simple. The sum of an empty list of course is 0. The sum of a non-empty list is the first element plus the sum of the rest of the list.
When we recursively call sum xs that call doesn't know what x is. It can't perform that addition. It can only sum the remaining list and then send that back up the stack for the addition to happen.
If we do this with a small list, it's not really an issue. The stack is limited, but not limited enough for that to be a concern.
But if we tried it with a list with a much larger number of elements, we'll run out of stack space and get an error.
The tail-recursive version of the sum function needs to make sure that its last call can provide a complete answer with no need to return info back up the stack. That means when the function is called, the caller needs to pass any required information to the called via its argument(s).
To accomplish this, we use an accumulator, which is commonly called acc.
let rec tail_sum acc lst =
match lst with
| [] -> acc
| x::xs -> tail_sum (acc + x) xs
As tail_sum iterates over the list, it keeps a running accumulation of the sum. When the list is empty, it returns that accumulator. We just need to always call tail_sum with a starting accumulator of 0.
tail_sum 0 [1; 2; 3; 4; 5; 6]
This is ugly, so you'll commonly see this hidden by using a locally nested helper function.
let tail_sum lst =
let rec helper acc lst =
match lst with
| [] -> acc
| x::xs -> helper (acc + x) xs
in
helper 0 lst
Imagine I work in an office. My boss asks me to find out when his lunch is going to be ready.
Scenario 1: I ask my secretary to tell me when lunch will be ready. He asks the cafe manager to tell him when lunch will be ready. The cafe manager asks the chef to tell her when lunch will be ready. They all report back to each other, and eventually to me, and I tell the boss when lunch will be ready.
Scenario 2: I ask my secretary to tell the boss when lunch will be ready. My secretary tells the cafe manager to tell the boss when lunch is ready. The cafe manager tells the chef to tell the boss when lunch is ready. The chef directly contacts the boss with the requested information.
The more direct approach in scenario 2 is possible because the information about the end recipient is passed along at each stage. The same goal is achieved, but this time once it has exited each person's hands, they can forget about it. They are no longer necessary to achieving the goal.
A tail call is the last expression in a function, which becomes the result of the whole function, e.g.,
let foo x = x + 1
let bar x =
let x = x + 1 in
foo x (* here foo is in the tail position *)
When a call is in the tail position, it could be made very cheaply, without allocating any administrative data on the program stack, which is usually necessary for more general calls, e.g.,
let baz x =
1 + foo x (* foo is not in the tail position! *)
In this example, we see that <...> + <...> is the last expression, so the computer needs to evaluate both sides of + and store them in some place in the memory before it can finally sum them up. This "some place in the memory" is usually the stack, which is a scarce resource on most modern operating systems. The scarcity of the stack becomes especially important if we make such calls iteratively.
Finally, if a tail call is recursive, it is called a tail-recursive call. Tail-recursive calls are very efficient in the implementation of iterative procedures, where instead of relying on specialized ad-hoc control-flow structures, like for and while, we can express the repetitiveness of the process in a more legible way, e.g.,
let rec contains_element k = function
| [] -> false
| x :: _ when k = x -> true
| _ :: xs -> contains_element k xs
describes naturally that a list contains an element k if either its head is k or if the tail of the list contains the element k. This algorithm is also a good showcase for an exception to the above-specified rule that a tail calls must be the last call in the function,
(* this version is also tail recursive! *)
let rec contains_element k = function
| [] -> false
| x :: xs -> x = k || contains_elements xs
It is because OCaml treats a call on the right-hand side of a short-circuit operator1 as a tail call, since you don't need to compute both operands before applying the operator.
1) which are || and && in OCaml that stand for logical disjunction (OR) and conjunction (AND).

Haskell: Handling deadlocked self-referential lists

Is there any useful reason why the GHC allows the following to block forever:
list = 1 : tail list
It seems with a bit of sophistication in the list iterator/generator we should be able to do something more useful:
Return error "Infinitely blocking list"
Return [1,1]
Explaining 2: it seems possible that when entering the generator to get element N, we could then make all self references inside the generator limited to the list but ending at N-1 (we notice the read N inside the scope generate N and return the end-of-list). It's a sort of simple deadlock detection using scopes.
Clearly this isn't that useful for the toy example above, but it may allow for more useful/elegant finite, self-referential list definitions, for example:
primes = filter (\x -> none ((==0).mod x) primes) [2..]
Note that either change should only affect list generators that would currently result in an infinite-block, so they seem backward compatible language changes.
Ignoring the GHC-complexity required to make such a change for a moment, would this behavior break any existing language behavior that I am missing? Any other thoughts on the "elegance" of this change?
Also see another BFS example that could benefit below. To me, this seems more functional/elegant than some other solutions, since I am only needing to define what a bfsList is, not how to generate it (i.e specifying a terminating condition):
bfs :: (a -> Bool) -> (a -> [a]) -> [a] -> Maybe a
bfs predf expandf xs = find predf bfsList
where bfsList = xs ++ concatMap expandf bfsList
Here is a denotational perspective on how list = 1 : ⊥.
First, a little background. In Haskell, values are partially ordered by "definedness", where values inolving &bot; ("bottom") are less-defined than ones without. So
⊥ is less defined than 1 : ⊥
1 : ⊥ is less defined than 1 : 2 : 3 : []
But it's a partial order, so
1 : ⊥ is not less defined than 2 : 3 : ⊥, nor is it more defined.
even though the second list is longer. 1 : ⊥ is only less defined than lists that start with 1. I highly recommend reading about denotational semantics of Haskell.
Now to your question. Look at
list = 1 : tail list
as an equation to be solved instead of a "function declaration". We rewrite it like this:
list = ((1 :) . tail) list
Viewing it this way, we see that list is a fixed point
list = f list
where f = (1 :) . tail. In Haskell semantics, recursive values are solved by finding the least fixed point according to the above ordering.
The way to find this is very simple. If you start with ⊥, and then apply the function over and over, and you will find an increasing chain of values. The point at which the chain stops changing will be the least fixed point (technically the it will be the limit of the chain, since it might not ever stop changing).
Starting with ⊥,
f ⊥ = ((1 :) . tail) ⊥ = 1 : tail ⊥
we see that ⊥ is not already a fixed point because we didn't get ⊥ out the other end. So let's try again with what we got out:
f (1 : tail ⊥) = ((1 :) . tail) (1 : tail ⊥)
= 1 : tail (1 : tail ⊥)
= 1 : tail ⊥
Oh look, it's a fixed point, we got the same thing out that we put in.
The important point here is that it's the least one. Your solution [1,1] = 1:1:[] is also a fixed point, so it solves the equation:
f (1:1:[]) = ((1 :) . tail) (1:1:[])
= 1 : tail (1:1:[])
= 1:1:[]
But of course, every list that starts with 1 is a solution, and it's unclear how we should choose between them. However, the one we found by recursion 1:⊥ is less defined than all of them, it delivers no more information than required by the equation, and that is the one that is specified by the language.
Even though list loops forever under GHCi, a proper binary compiled with GHC does detect the loop and signals an error. If you compile and run:
list = 1 : tail list
main = print list
it terminates with the error message:
Loop: <<loop>>
It does the same thing with your primes examples.
As others have noted, GHC doesn't detect all possible loops. It if did, then it would solve the Halting Problem, and that would probably make Haskell much more popular.
The reason it returns an error (or "gets stuck") instead of returning [1,1] is because the expression:
list = 1 : tail list
has well defined semantics in the Haskell language. These semantics assign it a value, and this value is "bottom" (or "error" or the symbol _|_), just as surely as the value of head [1,2,3] is 1.
(Well, technically, the value of list is 1 : _|_ which is "almost bottom". This is what #Justin Li was talking about in his comment. I've tried to give an explanation of why it has this value below.)
Though you may not see the use of a program or an expression that returns bottom and not see the harm in assigning non-bottom semantics to such expressions on the basis that it is "backwards compatible", most people in the Haskell community (the language designers, compiler developers, and experienced users) will disagree with you, so don't expect to make much progress with them.
As for the specific new semantics you are proposing, they are unclear. Why isn't the value of list equal to [1]? It seems to me that when I am entering the "generator" to get element n=1 (zero indexed, so the second element) and evaluate tail list, then the list ending at element n-1=0 is [1] which has tail equal to [], so I think I should get the following, right?
list = 1 : tail list
= 1 : tail [1] -- use list-so-far
= 1 : []
= [1]
Why the value is (almost) bottom
Here's why the value of list is (almost) bottom, according to the semantics of standard Haskell (but see note at the end).
For reference, the definition of tail is, effectively:
tail l = case l of _:xs -> xs
[] -> error "ack, you dummy!"
Let's try to "fully" evaluate list using Haskell semantics:
-- evaluating `list` using definition of `list`
list = 1 : tail list
-- evaluating `tail list` using definition of `tail`
list = 1 : case list of _:xs -> xs
...
-- evaluating case construct requires matching `list` to
-- a pattern, this requires evaluation of `list` using its defn
list = 1 : case (1 : tail list) of _:xs -> xs
...
-- case pattern match succeeds
list = 1 : let xs = tail list in xs -- just to be clear
= 1 : tail list
-- awesome, now all we need to do is evaluate:
list = 1 : tail list
-- ummm, Houston, we have a problem
and that infinite loop at the end is why the expression is "almost bottom".
Note: There are actually several different sets of Haskell semantics, different methods of calculating the values of Haskell expressions. The gold standard are the denotational semantics described in #luqui's answer. The ones I'm using above are, at best, a form of the "informal semantics" described in the Haskell report, but they're good enough to get the right answer.

Iterate produces StackOverflow errors

So I just started out with Frege and Haskell as well. I have experience with functional languages, since I was using Clojure for a couple of years now.
The first thing I wanted to try out is my usual approach at the Fibonacci numbers.
next_fib (a, b) = (b, a + b)
fibs = map fst $ iterate next_fib (0, 1)
fib x = head $ drop x fibs
This is how it turned out in Frege. It works, but for very high numbers for fib, e.g. (fib 4000), it throws StackOverflow errors. This surprised me, because same functions in Clojure would work just fine. Is this a Frege bug or am I getting the whole lazy evaluation thing wrong?
You probably don't "get the whole lazy evaluation thing wrong", but you're bitten twice by too lazy evaluation in this case.
And although GHC essentially works exactly the same as Frege in this regard, the outcome is different and seemingly unfavorable for Frege.
But the reason that Haskell can get awya with really big thunks[see below], while Frege early aborts with stack overflow is the way the runtime systems manage heap and stack. The Haskell RTS is flexible and can devote huge portions of the available memory to the stack, if the need arises. While Frege's runtime system is the JVM, which usually starts out with a tiny stack, just enough to accomodate a call depth of a few hundred. As you have observed, giving the JVM enough stack space makes the think work, exactly like it would in GHC.
Because of the ever scarce stack space in the JVM, we have developed some techniques in Frege to avoid unwanted and unneeded laziness. Two of them will be explained below. In the end, in Frege you are forced to control bad effects of laziness early, while the GHC developer can happily code away without having to take notice.
To understand the following, we need to introduce the concept "thunk". A thunk is first and foremost some yet to be evaluated expression. For example, since tuples are lazy, an expression like
(b, b+a)
is compiled to an application of the tuple constructor (,) to b and {a+b} where the notation { e } for the sake of this discussion means some implementation dependent representation of a thunk that promises to compute the expression e when evaluated. In addition, a thunk memoizes its result upon evaluation, so whenver the same thunk is evaluated again, it just returns the precomputed result. (This is only possible in a pure functional language, of course.)
For example, in Frege, to represent thunks there is a class Delayed<X> that implements Callable<X> and arranges for memoization of the result.
We shall now investigate what the result of
next_fib (next_fib (0, 1))
is. The inner application results in:
(1, {0+1})
and then the outer one computes from that:
({0+1}, {1+{0+1}})
We see here that thunks can get nested in other thunks, and this is the problem here, since every application of next_fib will result in a tuple that will have as its elements thunks that have thunks of the previous iteration nested inside them.
Now consider what is happening when the thunk for the 4000th fib-number gets evaluated, which happens, for instance, when you print it. It will have to perform an addition, but the numbers to add are actually both thunks, which must be evaluated before the addition can take place. In this way, each nested thunk means an invocation of that thunks evaluation method, unless the thunk is already evaluated. Hence, to print the 4000th number, we need a stack depth of at least 4000 in the case when no other thunk of this series was evaluated before.
So the first measure was to replace the lazy tuple constructor with the strict one:
(b; a+b)
It doesn't build thunks but computes the arguments right away. This is not available in Haskell, to do the same there you need to say something like:
let c = a+b in b `seq` c `seq` (b,c)
But this was not the end of the story. It turned out that the computation fib 4000 still overflowed the stack.
The reason is the implementation of iterate that goes like this:
iterate f x = x : iterate f (f x)
This builds an infinite list
[ x, f x, f (f x), f (f (f x)), ...]
Needless to say, all the terms except the first one are thunks!
This is normally not a problem when the list elements are evaluated in sequential order, because when, for example, the 3rd term
{f {f x}}
gets evaluated, the inner thunk is already evaluated and returns the result right away. In general, we need only enough stack depth to reach the first previously evaluated term. Here is a demo straight from the frege online REPL at try.frege-lang.org
frege> next (a,b) = (b; a+b) :: (Integer, Integer)
function next :: (Integer,Integer) -> (Integer,Integer)
frege> fibs = map fst $ iterate next (0,1)
function fibs :: [Integer]
frege> fib = (fibs!!)
function fib :: Int -> Integer
frege> map (length . show . fib) [0,500 ..]
[1,105,209,314,418,523,627,732,836,941,1045,1150,...]
frege> fib 4000
39909473435004422792081248094960912600792...
Here, with the map, we force evaluation of every 500th number (as far as the REPL demands output, it will only print initial portions of infinite lists), and compute the length of the decimal representation of each number (just so as not to display the large resulting numbers). This, in turn forces evaluation of the 500 preceding numbers, but this is ok, as there is enough stack space for that. Once that is done, we can even compute fib 4000! Because now, all the thunks up to 6000 are already evaluated.
But we can do even better with a slightly better version of iterate, which uses the head strict constructor (!:):
a !: as = a `seq` (a:as)
This evaluates the head of the list right away, which is appropriate in our case.
With the two changes, we get a program whose stack demand does not depend on the argument of fib anymore. Here is the proof:
frege> iterate' f x = x !: iterate' f (f x)
function iterate' :: (a->a) -> a -> [a]
frege> fibs2 = map fst $ iterate' next (0,1)
function fibs2 :: [Integer]
frege> (length . show . (fibs2 !!)) 4000
836
frege> (length . show . (fibs2 !!)) 8000
1672
frege> (length . show . (fibs2 !!)) 16000
3344
frege> (length . show . (fibs2 !!)) 32000
6688
frege> (length . show . (fibs2 !!)) 64000
13375
frege> (length . show . (fibs2 !!)) 128000
java.lang.OutOfMemoryError: Java heap space
Well, we'd need more heap space now to keep more than 100.000 huge numbers. But notice that there was no stack problem anymore to compute 32.000 new numbers in the last step.
We could get rid of the heap space problem with a simple tail recursive definition that doesn't need to mark all those numbers:
fib :: Int -> Integer
fib n = go n 0 1 where
go :: Int -> Integer -> Integer -> Integer
go 0 !a !b = a
go n !a !b = go (n-1) b (a+b)
I guess this would be even faster than traversing the list.
Unlike(?) in Clojure, direct list access is O(n), and long lists consume lots of space. Therefore, if you need to cache something and have an upper limit, you better use arrays. Here are 2 ways to construct an array of 10000 fibs:
frege> zzz = arrayFromList $ take 10000 $ map fst $ iterate (\(a,b) -> (b; a+b)) (0n,1)
function zzz :: JArray Integer
frege> elemAt zzz 4000
39909473435004422792081248094960912600792570982820257 ...
This works, because the intermediate list should never exist as a whole. And once created, access is O(1)
And there is also a special function for creating caches like that:
yyy = arrayCache f 10000 where
f 0 a = 0n
f 1 a = 1n
f n a = elemAt a (n-1) + elemAt a (n-2)
fib = elemAt yyy
This avoids even the intermediate list, all the tuples, and so on.
This way, you can keep your good habit of prefering combinators over explicit recursion. Please give it a try.

Pack consecutive duplicates of list elements into sublists in Ocaml

I found this problem in the website 99 problems in ocaml. After some thinking I solved it by breaking the problem into a few smaller subproblems. Here is my code:
let rec frequency x l=
match l with
|[]-> 0
|h::t-> if x=[h] then 1+(frequency x t)
else frequency x t
;;
let rec expand x n=
match n with
|0->[]
|1-> x
|_-> (expand x (n-1)) # x
;;
let rec deduct a b=
match b with
|[]-> []
|h::t -> if a=[h] then (deduct a t)
else [h]# (deduct a t)
;;
let rec pack l=
match l with
|[]-> []
|h::t -> [(expand [h] (frequency [h] l))]# (pack (deduct [h] t))
;;
It is rather clear that this implementation is overkill, as I have to count the frequency of every element in the list, expand this and remove the identical elements from the list, then repeat the procedure. The algorithm complexity is about O(N*(N+N+N))=O(N^2) and would not work with large lists, even though it achieved the required purpose. I tried to read the official solution on the website, which says:
# let pack list =
let rec aux current acc = function
| [] -> [] (* Can only be reached if original list is empty *)
| [x] -> (x :: current) :: acc
| a :: (b :: _ as t) ->
if a = b then aux (a :: current) acc t
else aux [] ((a :: current) :: acc) t in
List.rev (aux [] [] list);;
val pack : 'a list -> 'a list list = <fun>
the code should be better as it is more concise and does the same thing. But I am confused with the use of "aux current acc" in the inside. It seems to me that the author has created a new function inside of the "pack" function and after some elaborate procedure was able to get the desired result using List.rev which reverses the list. What I do not understand is:
1) What is the point of using this, which makes the code very hard to read on first sight?
2) What is the benefit of using an accumulator and an auxiliary function inside of another function which takes 3 inputs? Did the author implicitly used tail recursion or something?
3) Is there anyway to modify the program so that it can pack all duplicates like my program?
These are questions mostly of opinion rather than fact.
1) Your code is far harder to understand, in my opinion.
2a) It's very common to use auxiliary functions in OCaml and other functional languages. You should think of it more like nested curly braces in a C-like language rather than as something strange.
2b) Yes, the code is using tail recursion, which yours doesn't. You might try giving your code a list of (say) 200,000 distinct elements. Then try the same with the official solution. You might try determining the longest list of distinct values your code can handle, then try timing the two different implementations for that length.
2c) In order to write a tail-recursive function, it's sometimes necessary to reverse the result at the end. This just adds a linear cost, which is often not enough to notice.
3) I suspect your code doesn't solve the problem as given. If you're only supposed to compress adjacent elements, your code doesn't do this. If you wanted to do what your code does with the official solution you could sort the list beforehand. Or you could use a map or hashtable to keep counts.
Generally speaking, the official solution is far better than yours in many ways. Again, you're asking for an opinion and this is mine.
Update
The official solution uses an auxiliary function named aux that takes three parameters: the currently accumulated sublist (some number of repetitions of the same value), the currently accumulated result (in reverse order), and the remaining input to be processed.
The invariant is that all the values in the first parameter (named current) are the same as the head value of the unprocessed list. Initially this is true because current is empty.
The function looks at the first two elements of the unprocessed list. If they're the same, it adds the first of them to the beginning of current and continues with the tail of the list (all but the first). If they're different, it wants to start accumulating a different value in current. It does this by adding current (with the one extra value added to the front) to the accumulated result, then continuing to process the tail with an empty value for current. Note that both of these maintain the invariant.

foldl vs foldr: which should I prefer?

I remember that when I showed some code that I wrote to my professor he remarked, offhand, that
It rarely matters, but it's worth noting that fold* is a little bit more efficient than fold*' in SML/NJ, so you should prefer it over fold* when possible.
I forget whether fold* was foldr or foldl. I know that this is one of those micro-optimization things that probably doesn't make a big difference in practice, but I'd like to be in the habit of using the more efficient one when I have the choice.
Which is which? My guess is that this is SML/NJ specific and that MLton will be smart enough to optimize both down to the same machine code, but answers for other compilers are good to know.
foldl is tail-recursive, while foldr is not. Although you can do foldr in a tail-recursive way by reversing the list (which is tail recursive), and then doing foldl.
This is only going to matter if you are folding over huge lists.
Prefer the one that converts the given input into the intended output.
If both produce the same output such as with a sum, and if dealing with a list, folding from the left will be more efficient because the fold can begin with head element, while folding from the right will first require walking the list to find the last element before calculating the first intermediate result.
With arrays and similar random access data structures, there's probably not going to be much difference.
A compiler optimization that always chose the better of left and right would require the compiler to determine that left and right were equivalent over all possible inputs. Since foldl and foldr take a functions as arguments, this is a bit of a tall order.
I'm going to keep the accepted answer here, but I had the chance to speak to my professor, and his reply was actually the opposite, because I forgot a part of my question. The code in question was building up a list, and he said:
Prefer foldr over foldl when possible, because it saves you a reverse at the end in cases where you're building up a list by appending elements during the fold.
As in, for a trivial example:
- val ls = [1, 2, 3];
val ls = [1,2,3] : int list
- val acc = (fn (x, xs) => x::xs);
val acc = fn : 'a * 'a list -> 'a list
- foldl acc [] ls;
val it = [3,2,1] : int list
- foldr acc [] ls;
val it = [1,2,3] : int list
The O(n) save of a reverse is probably more important than the other differences between foldl and foldr mentioned in answers to this question.