Maybe I should submit an issue for this, but I thought I should get some discussion and feedback first.
Whether Batteries.LazyList lists are equal, or instead generate an exception, depends on whether the lists have been previously been evaluated:
# module LL = Batteries.LazyList;;
# let ns = LL.seq 0 ((+) 1) ((>) 3);;
# let ns' = LL.seq 0 ((+) 1) ((>) 3);;
# ns = ns';;
Exception: Invalid_argument "compare: functional value".
Raised by primitive operation at unknown location
Called from file "toplevel/toploop.ml", line 180, characters 17-56
# LL.(ns = ns');; (* maybe there's a LazyList version of = ? *)
Exception: Invalid_argument "compare: functional value".
Raised by primitive operation at unknown location
Called from file "toplevel/toploop.ml", line 180, characters 17-56
# LL.last ns, LL.last ns';; (* view last element, force realization *)
- : int * int = (2, 2)
# ns = ns';;
- : bool = true
For a list equality test to either raise an exception or return a legitimate result depending on how the list has been accessed previously seems to me arbitrary and bug-conducive. It seems as if what should happen when =
is called is that all of the nodes should be evaluated, just as when last
is called. (I suppose that another alternative would be to return a lazy result that needs to be forced, but I would not expect =
to do that.)
I see in the source that LazyList
is implemented using OCaml’s Lazy
module. I don’t fully understand the Lazy
module source, but I gather that it uses thunks to represent not-yet-computed elements. If a list node has not yet been evaluated, then, the equality comparison is between two functions. Since =
is supposed to raise an exception when comparing functions, this explains why the first equality tests above raise the exception they do. However, it still seems bad that the test raises an exception or succeeds depending on context.
One might say, “Well what do you expect from a lazy list? It changes form depending on past accesses. Use at your own risk.” I don’t agree for this case. Equality is a very basic thing to expect to work (or not, if comparing functions). (Clojure lazy sequences never do this, btw, except maybe in some unusual cases involving lazy sequences embedded in lazy sequences.)
One might also say that lazy lists shouldn’t be compared for equality because they might be infinite. However, that would also be an argument against a last
function. Yes, infinite lists can cause a problem, but I do feel that then it is the programmer’s responsibility to avoid the problems. Making =
raise an exception won’t solve other problems. That is a risk that you take on if you use infinite lazy lists. (I do take on that risk, btw. I find lists of unspecified length convenient.)