ClojureScript

filter on lazy seq causes stack overflow

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Completed
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • 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 ##

Activity

Hide
Lars Bohl added a comment -

Affect clojurescipt version 0.0-1859

Show
Lars Bohl added a comment - Affect clojurescipt version 0.0-1859
Hide
Lars Bohl added a comment -

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?

Show
Lars Bohl added a comment - 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?
David Nolen made changes -
Field Original Value New Value
Resolution Completed [ 1 ]
Status Open [ 1 ] Resolved [ 5 ]
David Nolen made changes -
Status Resolved [ 5 ] Closed [ 6 ]

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: