1

Batteries.LazyList allows one to define lazy lists. I would like to define a lazy list consisting of x, f x, f (f x), f (f (f x)), etc.

Based on comments in the module documentation, it appears that from_loop is the function I want:

"from_loop data next creates a (possibly infinite) lazy list from the successive results of applying next to data, then to the result, etc."

This description suggests that if I wanted a lazy list of non-negative integers, for example, I could define it like this:

let nat_nums = from_loop 0 (fun n -> n + 1)

However, this fails because the signature of from_loop is

'b -> ('b -> 'a * 'b) -> 'a LazyList.t

so the next function has signature ('b -> 'a * 'b). In utop, the error message underlines n + 1 and says

Error: This expression has type int but an expression was expected of type 'a * int

I don't understand what 'a is supposed to be. Why is the next function supposed to return a pair? Why is the type of the list supposed to be a 'a LazyList.t? Shouldn't the type of the elements be the same as the type of the argument to the next function? The description of the function doesn't make the answers clear to me.


In case it's helpful, my conception of what I'm trying to do comes from Clojure's iterate. In Clojure I could create the above definition like this:

(def nat-nums (iterate (fn [n] (+ n 1)) 0))
Mars
  • 8,689
  • 2
  • 42
  • 70

2 Answers2

2

The function passed to from_loop has to return a pair. The first element of the pair is the value you want to return. The second element of the pair is the state required to calculate the next element later on.

Your code:

(fun n -> n + 1)

Just calculates the next element of the lazy list, it doesn't return the state required for the next call. Something like this is what is wanted:

(fun n -> (n, n + 1))

(This will return a list starting with 0, which I think is what you want.)

This formulation is more flexible than your clojure example, because it allows you to maintain arbitrary state distinct from the values returned. The state is of type 'b in the type you give for from_loop.

I don't have Batteries right now, so I can't try this out. But I think it's correct based on the types.

Jeffrey Scofield
  • 65,646
  • 2
  • 72
  • 108
  • Thanks Jeffrey. I did some experiments based on what you wrote, and this seems to be the right variant: `LazyList.from_loop 0 (fun n -> (n, n+1))`. If I use `n+1` for both elements of the pair, the first element ends up being 1 rather than 0. – Mars Apr 30 '17 at 05:54
  • (I already fixed that up, I made a few small edits like that. Sorry for moving target.) – Jeffrey Scofield Apr 30 '17 at 05:55
  • Ah, right. Thanks. And thanks for the explanation of why there's a separate state returned. Very nice. I see the value of that. (No problem about moving target. I always edit after I post, too. Probably should have waited to comment.) – Mars Apr 30 '17 at 05:56
0

It turns out that the function that I really wanted was LazyList.seq, not from_loop. While from_loop has its uses, seq is simpler and does what I wanted. The only trick is that you have to provide a third argument which is a termination test that returns false when the list should end. I wanted an infinite list. One can create that using use a termination function that always returns true:

let nat_nums = seq 0 (fun n -> n + 1) (fun _ -> true);;
LazyList.to_list (LazyList.take 8 nat_nums);;
- : int list = [0; 1; 2; 3; 4; 5; 6; 7]
Mars
  • 8,689
  • 2
  • 42
  • 70