Fork me on GitHub

Is there anything about nth that requires it to realize the sequence it operates upon? In terms of a concrete example, this (sketch) of an alternative nth doesn't

(defn nth' [coll n] (transduce (drop n) (completing #(reduced %2)) nil coll))
if applied to something directly reducible (like the result of iterate). Why am I asking? ClojureScript currently doesn't have locals clearing, and an nth that behaves this way is useful in situations where n is huge and the head holding will exhaust the heap. I can imagine reasons for not ever having core nth behave this way, but I'm asking simply to help clarify my thinking on the subject.


to me "realize the sequence" implies the construction of the sequence even if it is immediately thrown away, which that will still do for lazy seqs


when applied to something directly reducible, that still steps through the intervening items


Maybe a better definition of that is realized? returning true, and things not effectively acting like eduction, recalculating, etc.


which, technically isn't realizing a sequence, but it can be hard to make a meaningful distinction


I am not sure I follow, realized? (at least in clojure) seems pretty useless for reasoning about this kind of thing


if you really want to optimize nth for non-seq things, what not give range a version of nth that does it closed form?


Fair enough. Here is a motivating concrete example. Consider f, which may be pure, but expensive. The above nth' is nice on memory in ClojureScript if you do, say (nth (iterate f x) 100000)

Alex Miller (Clojure team)00:12:32

in seq worlc, nthrest / nthnext are used to avoid some portion of the realization cost

Alex Miller (Clojure team)00:12:23

I think you could use halt-when for something like this in a reducible context too


in clojure nth is backed by the Indexed interface, so you could have your reducible type implement what efficient nth you want


it sounds like the root problem is the lack of locals clearing, not some special optimization of nth though


Yes, really, to fix this ClojureScript should implement locals clearing.


Revising nth to workaround such a thing would be nothing more than a workaround. But, it got me thinking about whether it is even legitimate to consider nth behaving that way.


Put it another way, if someone asked: Why can't nth behave that way, it would be nice if there was a specific concrete reason that prohibited it.


This is mostly a mental exercise, I'm not pushing to have nth revised in ClojureScript, and it would even more put my mind at rest if I knew there was a prohibition of some sort.


does clojurescript have an analog to clojure.lang.Indexed?


Yeah, cljs.core/IIndexed, which is what nth is for.


And yes, iterate returns an Iterate that could implement IIndexed...


so any type can choose to provide a different nth implementation, which is a similar but not the same thing as could the generic nth implementation be different


(That would be odd though, because it is supposed to be O(1) , right? Hrm.)


Actually, nah, nth can be linear.


ah, actually that might be a thing, if it implements Indexed it is supposed to be O(1), and if doesn't implement Indexed it is O(n) via seq


My primary argument against making nth behave this way is that it would introduce recalculating in some code where things are currently cached, so in my mind I don't think it should ever happen in ClojureScript. But it sounds like, apart from that perf concern, it may actually be an open question on whether nth could be like nth' above.


By the way, halt-when is new to me. I must have missed it. Gonna definitely check that one out. 🙂