<< Back to previous view

[CLJS-587] filter on lazy seq causes stack overflow Created: 10/Sep/13  Updated: 26/Sep/13  Resolved: 26/Sep/13

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Lars Bohl Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: None
Environment:

Tested in firefox 21.0 and chromium 27.0.1453.93



 Description   

This expression causes a stack overflow, instead evaluating to 9999:

(first (filter #(= 9999 %) (range)))

These expressions should be equivalent, and do not cause a stack overflow:

(first (filter #(= 9999 %) (vec (take 10000 (range)))))
(some #(when (= 9999 %) %) (range))

Thus, it seems as if filter cannot handle an input that's already lazy.

More elaborate version: https://www.refheap.com/18490

See also this google group thread: https://groups.google.com/forum/#!topic/clojurescript/3NlQOxwNrRc

    1. Please consider using ssl on dev.clojure.org ##


 Comments   
Comment by Lars Bohl [ 10/Sep/13 1:06 AM ]

Affect clojurescipt version 0.0-1859

Comment by Lars Bohl [ 15/Sep/13 4:01 AM ]

Using a javascript debugger (chromium built-in) on the (first (filter #(= 9999 %) (range))) call, I found out that this is the concrete implementation of "first" that gets called:

cljs.core.LazySeq.prototype.cljs$core$ISeq$_first$arity$1 = function(coll) { var self__ = this; return cljs.core.first.call(null, cljs.core.lazy_seq_value.call(null, coll)) };

where this is lazy_seq_value:

cljs.core.lazy_seq_value = function lazy_seq_value(lazy_seq) {
var x = lazy_seq.x;
if(lazy_seq.realized) { return x }else { lazy_seq.x = x.call(null); lazy_seq.realized = true; return lazy_seq.x }
};

Compare this with clojure's java implemetation of "first" that gets called by the same code (https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LazySeq.java):

public Object first(){ seq(); if(s == null) return null; return s.first(); }

where seq is this defined like this:

final synchronized public ISeq seq(){
sval();
if(sv != null)
{
Object ls = sv;
sv = null;
while(ls instanceof LazySeq)

{ ls = ((LazySeq)ls).sval(); }

s = RT.seq(ls);
}
return s;
}

The while loop in LazySeq#seq seems to avoid the stack problems in clojure. Whereas clojurescript, there is no while loop in LazySeq's implementation of first. Maybe the problem will disappear if it is added there in some way?

Comment by David Nolen [ 26/Sep/13 3:36 PM ]

fixed http://github.com/clojure/clojurescript/commit/4396e31026338c1b347a0bf833a9077fde88d9a5

Generated at Sun Dec 21 02:33:04 CST 2014 using JIRA 4.4#649-r158309.