Agregar metadatos a una secuencia perezosa
-
20-08-2019 - |
Pregunta
Cuando intento agregar metadatos a una secuencia perezosa infinita en Clojure, obtengo un desbordamiento de pila, y si elimino los metadatos, entonces funciona bien. ¿Por qué agregar la macro with-meta
rompe la secuencia lenta?
Primero crea una secuencia infinita de un número muy agradable:
(defn good [] (lazy-seq (cons 42 (good)))) user> (take 5 (good)) (42 42 42 42 42)
Luego, agregue algunos metadatos a cada una de las instancias de lazy-seq:
(defn bad [] (lazy-seq (cons 42 (with-meta (bad) {:padding 4})))) user> (take 5 (bad)) java.lang.StackOverflowError (NO_SOURCE_FILE:0) [Thrown class clojure.lang.Compiler$CompilerException]
Intente subir los metadatos un nivel:
(defn also-bad [] (with-meta (lazy-seq (cons 42 (also-bad))) {:padding 4})) user> (take 5 (foo)) java.lang.StackOverflowError (NO_SOURCE_FILE:0) [Thrown class clojure.lang.Compiler$CompilerException]
Aquí hay un ejemplo de metadatos en una secuencia finita:
(defn also-works [] (lazy-seq (cons 4 (with-meta () {:a 5})))) user> (also-works) (4) user> (meta (rest (also-works))) {:a 5} user>
Solución
Porque un LazySeq
evalúa su cuerpo tan pronto como llame a withMeta
en el seq()
. Pierdes tu pereza.
public final class LazySeq extends Obj implements ISeq, List{
...
public Obj withMeta(IPersistentMap meta){
return new LazySeq(meta, seq());
}
...
}
with-meta
evalúa el cuerpo de la secuencia lenta si aún no se ha evaluado. Su código anterior sigue llamando a <=> en sucesivas secuencias perezosas, que las evalúa todas hasta que explota la pila. No creo que haya alguna forma de agregar metadatos a una secuencia perezosa sin hacer que evalúe su cuerpo.