ajout de métadonnées à une séquence lente
-
20-08-2019 - |
Question
Lorsque j'essaie d'ajouter des métadonnées à une séquence infinie de lazy dans Clojure, la pile déborde, et si je supprime les métadonnées, cela fonctionne parfaitement. Pourquoi l’ajout de la macro with-meta
interrompt-il la séquence paresseuse?
Créez d’abord une séquence infinie d’un très beau nombre:
(defn good [] (lazy-seq (cons 42 (good)))) user> (take 5 (good)) (42 42 42 42 42)
Ajoutez ensuite des métadonnées à chacune des instances 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]
Essayez de déplacer les métadonnées d'un niveau supérieur:
(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]
Voici un exemple de métadonnées sur une séquence finie:
(defn also-works [] (lazy-seq (cons 4 (with-meta () {:a 5})))) user> (also-works) (4) user> (meta (rest (also-works))) {:a 5} user>
La solution
Parce qu'un LazySeq
évalue son corps dès que vous appelez withMeta
sur le seq()
. Vous perdez votre paresse.
public final class LazySeq extends Obj implements ISeq, List{
...
public Obj withMeta(IPersistentMap meta){
return new LazySeq(meta, seq());
}
...
}
with-meta
évalue le corps du seq différé s'il n'a pas déjà été évalué. Votre code ci-dessus continue d’appeler <=> sur des séquences successives, qui les évaluent toutes jusqu’à ce que la pile explose. Je ne pense pas qu’il soit actuellement possible d’ajouter des métadonnées à un seq paresseux sans le forcer à évaluer son corps.