2009-06-28 8 views
3

Когда я пытаюсь добавить метаданные в бесконечную ленивую последовательность в Clojure, я получаю переполнение стека, и если я снимаю метаданные, то это работает отлично. Почему добавление макроса with-meta прерывает ленивый seq?Добавление метаданных в ленивую последовательность

Сначала нужно создать бесконечную SEQ из очень хорошего номера:

 
(defn good [] 
    (lazy-seq 
    (cons 42 
     (good)))) 

user> (take 5 (good)) 
(42 42 42 42 42) 

Затем добавить некоторые метаданные для каждого из ленивых-слы случаев:

 
(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] 

Попробуйте переместить мета-данные до одного уровень:

 
(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] 

Ниже приведен пример метаданных на конечной последовательности:

 
(defn also-works [] 
    (lazy-seq 
     (cons 4 
     (with-meta 
     () 
      {:a 5})))) 

user> (also-works) 
(4) 
user> (meta (rest (also-works))) 
{:a 5} 
user> 
+0

Оба lazy-seq и with-meta являются макросами, поэтому вы должны иметь возможность получить дополнительную информацию о макроэкспоненте и. MACROEXPAND-1. – Svante

ответ

6

Поскольку LazySeq оценивает свое тело, как только вы звоните withMeta на LazySeq. Вы теряете свою лень.

public final class LazySeq extends Obj implements ISeq, List{ 
    ... 
    public Obj withMeta(IPersistentMap meta){ 
     return new LazySeq(meta, seq()); 
    } 
    ... 
} 

seq() оценивает тело ленивого НомерСтарта, если он еще не был оценен. Ваш код выше продолжает звонить with-meta на последовательных ленивых секциях, которые оценивают их все до тех пор, пока стек не взрывается. Я не думаю, что в настоящее время есть способ добавить метаданные в ленивый seq, не заставляя его оценивать его тело.

+0

FWIW, второй патч в [CLJ-1800] (https://dev.clojure.org/jira/browse/CLJ-1800), если он будет принят, предоставит вам путь. –