generalized, tail-recursive left fold that can finish tne computation prematurely

Dear Haskellers, while playing with folds and trying to implement `!!` by folding, I came to the conclusion that: - `foldr` is unsuitable because it counts the elements from the end, while `!!` needs counting from the start (and it's not tail recursive). - `foldl` is also unsuitable, because it always traverses the whole list. I came up with the following tail-recursive generalization of `foldl` that allows exiting the computation prematurely: foldlE :: (a -> c) -> (a -> b -> Either c a) -> Either c a -> [b] -> c foldlE f g = fld where fld (Left c) _ = c fld (Right a) [] = f a fld (Right a) (x:xs) = fld (g a x) xs `foldl` can be defined from it as foldl'' :: (a -> b -> a) -> a -> [b] -> a foldl'' f z = foldlE id ((Right .) . f) (Right z) and `!!` as: -- Checks for a negative index omitted for brevity. index :: Int -> [a] -> a index i = foldlE (error $ "No such index") f (Right i) where f 0 x = Left x f n _ = Right (n - 1) Is something like that already available somewhere? Best regards, Petr Pudlak

Hi.
while playing with folds and trying to implement `!!` by folding, I came to the conclusion that:
- `foldr` is unsuitable because it counts the elements from the end, while `!!` needs counting from the start (and it's not tail recursive).
What is the problem with the following definition using foldr?
index :: Int -> [a] -> a index n xs = foldr (\ x r n -> if n == 0 then x else r (n - 1)) (const (error $ "No such index")) xs n
Cheers, Andres

* Petr Pudlák
Dear Haskellers,
while playing with folds and trying to implement `!!` by folding, I came to the conclusion that:
- `foldr` is unsuitable because it counts the elements from the end, while `!!` needs counting from the start (and it's not tail recursive). - `foldl` is also unsuitable, because it always traverses the whole list.
Every structurally-recursive function is definable through foldr, because foldr *is* the structural recursion (aka catamorphism) operation for lists. Here the trick is to make the accumulator a function. This way you can pass a value from left to right. Something like foldr (\x rest n -> ...) id list 0 I'll leave filling in the dots as an exercise. Roman

* Roman Cheplyaka
* Petr Pudlák
[2013-02-18 17:10:26+0100] Dear Haskellers,
while playing with folds and trying to implement `!!` by folding, I came to the conclusion that:
- `foldr` is unsuitable because it counts the elements from the end, while `!!` needs counting from the start (and it's not tail recursive). - `foldl` is also unsuitable, because it always traverses the whole list.
Every structurally-recursive function is definable through foldr, because foldr *is* the structural recursion (aka catamorphism) operation for lists.
Here the trick is to make the accumulator a function. This way you can pass a value from left to right.
Something like
foldr (\x rest n -> ...) id list 0
I'll leave filling in the dots as an exercise.
Er, my template is not quite right — I had 'length' in mind while writing it. Anyway, Andres showed the correct definition. Roman

Thanks Roman and Andres for the tip. I knew the trick with accumulating a
function, but I had never imagined it could work so efficiently. I thought
the problem with using foldr would be that the function would be neither
tail recursive nor efficient and so I hadn't even tried. Apparently that
was wrong. After your suggestion I checked its performance and how it
compiles to core and to my surprise GHC optimizes the whole thing into a
most-efficient tail recursive function!
Best regards,
Petr
2013/2/18 Roman Cheplyaka
* Petr Pudlįk
[2013-02-18 17:10:26+0100] Dear Haskellers,
while playing with folds and trying to implement `!!` by folding, I came to the conclusion that:
- `foldr` is unsuitable because it counts the elements from the end, while `!!` needs counting from the start (and it's not tail recursive). - `foldl` is also unsuitable, because it always traverses the whole list.
Every structurally-recursive function is definable through foldr, because foldr *is* the structural recursion (aka catamorphism) operation for lists.
Here the trick is to make the accumulator a function. This way you can pass a value from left to right.
Something like
foldr (\x rest n -> ...) id list 0
I'll leave filling in the dots as an exercise.
Roman

On 18/02/13 16:10, Petr Pudlák wrote:
- `foldr` is unsuitable because it counts the elements from the end, while `!!` needs counting from the start (and it's not tail recursive).
It is common misconception that foldr processes the list "from the right". foldr "brackets" from the right, but this has nothing to do with processing direction; all [a] are processed left to right, since this is the only way to structurally deconstruct them. This is the reason why it is possible to write foldr (:) [] [1..] If foldr processed the list from the right, it would on infinite lists - and it does.
participants (4)
-
Andres Löh
-
Niklas Hambüchen
-
Petr Pudlák
-
Roman Cheplyaka