Reputation: 28490
I was experimenting with the idea of repeatedly applying a function f
to a given argument z
, over and over, thus getting an infinite list.
Basically I want the list [z, f z, f (f z), f (f (f z)), ...]
, which I could feed to take n
, takeWhile whatever
, or something else.
Initially I thought that since the list I'm gonna deal with is infinite (I was thinking of repeat f
), I should use foldr
, but in reality that's not possible, because when foldr
ing an infinite list the accumulator is usually set to undefined
because unused, so in something like foldr (\f (x:xs) -> f x:x:xs) undefined (repeat f)
I wouldn't have where to put z
.
So I started writing this question, and, thanks to the automatic suggestions that pop up, I discovered that iterate
exists already, and its implementation is,
{-# NOINLINE [1] iterate #-}
iterate :: (a -> a) -> a -> [a]
iterate f x = x : iterate f (f x)
so my question changed: is it possible to write iterate
as a fold? If so, how? If not, why?
Upvotes: 4
Views: 525
Reputation: 116174
To me using repeat f
as you suggest amounts to "cheating", since you hide an infinite recursion in that infinite list. The proper way is to use anamorphisms (unfoldr
) as Silvio Mayolo already explained.
That being said, if that's allowed then we can implement iterate
as a fold. Here's an example where we obtain iterate ('b':) "X"
in GHCi:
> take 9 $ foldr (\ x xs k -> k : xs (x k) ) (\_ -> []) (repeat ('b':)) "X"
["X","bX","bbX","bbbX","bbbbX","bbbbbX","bbbbbbX","bbbbbbbX","bbbbbbbbX"]
This uses the (in-)famous trick "calling foldr
with four arguments". The idea is to make it so that the fold returns a function, which we eventually feed with the initial value of iterate
, namely "X"
. This allows us to modify that fourth argument in each "recursive call": when we write xs (x k)
we are essentially calling xs
, the function computed by the fold on the rest of the list, with x k
which is actually 'b':k
, effectively passing the next value in the iteration list.
One of the reasons I don't like this approach is that we don't even need repeat f
. Indeed, repeat ()
would be enough to drive the fold.
> take 9 $ foldr (\ _ xs k -> k : xs ('b':k) ) (\_ -> []) (repeat ()) "X"
["X","bX","bbX","bbbX","bbbbX","bbbbbX","bbbbbbX","bbbbbbbX","bbbbbbbbX"]
Even worse: if we have access to repeat ()
we can write out a fixed point combinator (for function types):
> myFix f = foldr (\ _ xs -> f . xs) id (repeat ()) id
> :t myFix
myFix :: ((a -> a) -> a -> a) -> a -> a
Here's the factorial computed using the fixed point operator:
> myFix (\g n -> if n==0 then 1 else n * g (n-1)) 5
120
Upvotes: 4
Reputation: 70357
No, or at least not in any idiomatic way. But that's because foldr
is the wrong tool for the job. foldr
is a fold, or catamorphism. That's a fancy mathematical way of saying it takes aggregate data (in this case, a list) and produces a single result (a scalar, such as a number). This is why operations like sum
are straightforward to write in terms of foldr
, because sum
fundamentally takes aggregate data and produces a singular result.
What you want is an anamorphism, or a way to take a single point of data (z
in your example) and produce aggregate data from that. In Haskell, this is appropriately named unfoldr
.
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
unfoldr
starts with ab initial value b
and calls the function it's given. Each time it calls the function, if it produces a Just (a, b')
, then we use a
as the first element of the list and continue with b'
as the state value. If the function returns Nothing
, then we stop iterating.
In your case, you want to produce an infinite list, so we'll always return a Just
value. You can write iterate
in terms of unfoldr
as follows.
import Data.List(unfoldr)
iterate' :: (a -> a) -> a -> [a]
iterate' f = unfoldr (\z -> Just (z, f z))
Upvotes: 12