(empty lazyseq)
does not copy metadata. This seems like a bug?
can you give an example?
next
and rest
won't copy metadata either -- is this specifically about the case where lazyseq
is empty?
I assume at the moment empty
doesn't even check if the arg is empty, and for a lazy seq that check could potentially block forever
(def x (with-meta (map inc (range 10)) {:foo :bar}))
=> #'user/x
(meta x)
=> {:foo :bar}
(meta (empty x))
=> nil
the real example is from a json array decoded by cheshire, but it is also a clojure.lang.LazySeq
yeah I don't think seqs in general are expected to have the same metadata for different sub-sequences of them
because of the way cons
works, a seq is sort of like a vector but also sort of like a (linear) tree structure
in the tree view it makes as much sense to ask whether next
or empty
should preserve metadata as it does to expect that when you pull a value out of a map that value should have the same metadata as the map
then why does lazy-seq have a metadata field at all?
there'd have to be a lot more copying to support metadata preservation;
like if I have a seq, and then tweak its metadata, that'd ultimately be a linear time operation instead of constant time, because it'd have to be tweaked for each next
why this?
(meta (with-meta (rest (map inc (range 10))) {:foo :bar}))
=> {:foo :bar}
well it at least has it because the compiler uses it for syntax
also I don’t see why getting metadata would realize a seq
symbols have metadata
I didn't say anything about realizing
> I assume at the moment empty
doesn’t even check if the arg is empty, and for a lazy seq that check could potentially block forever
I'm saying (with-meta (range 10) {:foo :bar})
is a linear-time operation
(empty lazy-seq) returns a persistentlist
I’m fine with that
that’s expected
that comment was about an alternate interpretation of your original question where you were only interested in preserving metadata when the arg was empty
but what’s not expected is that it doesn’t have the metadata of the original lazyseq
I realize it's surprising, and that's unfortunate, I'm just pointing out why changing this behavior would be problematic in other ways
I didn’t catch that. why would it be problematic?
so I'm talking about how metadata on seqs works more broadly does this do what you expect or would want?
user> (def x0 (with-meta () {:empty :meta}))
#'user/x0
user> (def x1 (with-meta (cons 1 x0) {:one :thing}))
#'user/x1
user> (meta x0)
{:empty :meta}
user> (meta x1)
{:one :thing}
user> (meta (rest x1))
{:empty :meta}
in particular the last two steps, where (meta x1)
is different from (meta (rest x1))
yes, that is all fine
I’m really more concerned with empty
though
so what should (meta (empty x1))
do for that particular x1
?
should it give you the metadata that x1
has, or the metadata that x0
has?
x1
I looked at the code and agree this is a bug
public IPersistentCollection empty(){
PersistentList o PersistentList.EMPTY;
return o.withMeta (o, meta ())
}
is basically what I expect this to be
(not tested, formatted weird because typed into a repl)
in general, most of the colls do retain metadata on empty
that seems to be missing on ASeq.empty() (which affects many subclasses) and LazySeq.empty()
some random subclasses do actually override and fix that, like ChunkedSeq.empty()
The reason I ask is because empty
doesn’t explicitly say “copies metadata” but I know bugs have been filed and fixed against it not copying in the past, and I also intuitively expect it to copy
in general, all collection operations are expected to retain metadata
(not all seq operations though)
empty is a collection operation though (and seqs are logical collections), so I think all that holds
do you have jira access?
yes
if you can file a ticket, that'd be great
NP, thank you @alexmiller https://clojure.atlassian.net/browse/CLJ-2566
thx
FYI, we have Clojure 1.10.2 Alpha 1 in production (as of yesterday morning) and -- shock, horror -- it's running just fine, as expected.