aggiunta di metadati a una sequenza pigra
-
20-08-2019 - |
Domanda
Quando provo ad aggiungere metadati a un'infinita sequenza pigra in Clojure, ottengo un overflow dello stack e se tolgo i metadati, allora funziona perfettamente. Perché l'aggiunta della with-meta
macro interrompe il pigro seq?
Per prima cosa crea un seq infinito di un numero molto bello:
(defn good [] (lazy-seq (cons 42 (good)))) user> (take 5 (good)) (42 42 42 42 42)
Quindi, aggiungi alcuni metadati a ciascuna delle istanze 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]
Prova a spostare i metadati di un livello superiore:
(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]
Ecco un esempio di metadati su una sequenza finita:
(defn also-works [] (lazy-seq (cons 4 (with-meta () {:a 5})))) user> (also-works) (4) user> (meta (rest (also-works))) {:a 5} user>
Soluzione
Perché un LazySeq
valuta il suo corpo non appena chiami withMeta
sul seq()
. Perdi la tua pigrizia.
public final class LazySeq extends Obj implements ISeq, List{
...
public Obj withMeta(IPersistentMap meta){
return new LazySeq(meta, seq());
}
...
}
with-meta
valuta il corpo del seq pigro se non è già stato valutato. Il tuo codice sopra continua a chiamare <=> su sequenze pigre successive, che le valuta tutte fino allo scoppio dello stack. Non credo ci sia attualmente alcun modo per aggiungere metadati a un seq pigro senza indurlo a valutare il suo corpo.