Reputation: 15199
When building lists, I usually use a right fold, as that lets me use the right-associative :
operator without affecting the order of the resulting list. In a left fold, I could use ++
, but I understand that this will entail repeatedly copying the list while it is being produced, giving O(N^2) operations for an N-element list, which is generally unacceptable.
So, when I have to use a left fold (in my specific case this is because I'm using foldlM
and the monadic actions produced must be performed in left-to-right order), is there any better way of reconciling this other than building the list in the fold using :
and reversing the result?
Upvotes: 3
Views: 331
Reputation: 77941
when I have to use a left fold (... because ... the monadic actions produced must be performed in left-to-right order)
Right fold can lean so far right that it comes back left again. For example, you can print (i.e. monadic action) each number and calculate partial sums of a list from left to right using a right fold:
fun :: [Int] -> IO [Int]
fun xs = foldr go (const $ return []) xs 0
where go x f a = let a' = a + x in print x >> (a' :) <$> f a'
then:
\> fun [1..5]
1
2
3
4
5
[1,3,6,10,15]
note that the output list is built using (a' :)
and the monadic action is performed left to right, even though it is a right fold.
Upvotes: 5
Reputation: 25763
Since you mention foldlM
, likely this blog post of mine answers your question in depth:
Constructing a list in a Monad
The bottom line is: Use difference lists; i.e. in your left-fold, you accumulate a value of type [a] -> [a]
, where you can append a value using . (x:)
efficiently, and at the end you apply this function to []
to obtain your list.
Upvotes: 3