clojure-dev

Issues: https://clojure.atlassian.net/browse/CLJ | Guide: https://insideclojure.org/2015/05/01/contributing-clojure/
favila 2020-03-25T12:49:25.000600Z

(empty lazyseq) does not copy metadata. This seems like a bug?

alexmiller 2020-03-25T12:59:41.000800Z

can you give an example?

2020-03-25T13:01:09.001400Z

next and rest won't copy metadata either -- is this specifically about the case where lazyseq is empty?

2020-03-25T13:01:40.002Z

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

favila 2020-03-25T13:03:41.002400Z

(def x (with-meta (map inc (range 10)) {:foo :bar}))
=> #'user/x
(meta x)
=> {:foo :bar}
(meta (empty x))
=> nil

favila 2020-03-25T13:04:15.003100Z

the real example is from a json array decoded by cheshire, but it is also a clojure.lang.LazySeq

2020-03-25T13:06:37.004600Z

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

favila 2020-03-25T13:07:39.005900Z

then why does lazy-seq have a metadata field at all?

2020-03-25T13:07:42.006100Z

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

favila 2020-03-25T13:07:45.006300Z

why this?

favila 2020-03-25T13:07:52.006700Z

(meta (with-meta (rest (map inc (range 10))) {:foo :bar}))
=> {:foo :bar}

2020-03-25T13:07:57.007Z

well it at least has it because the compiler uses it for syntax

favila 2020-03-25T13:08:09.007600Z

also I don’t see why getting metadata would realize a seq

2020-03-25T13:08:13.007700Z

symbols have metadata

2020-03-25T13:08:24.008100Z

I didn't say anything about realizing

favila 2020-03-25T13:08:42.008800Z

> 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

2020-03-25T13:08:45.008900Z

I'm saying (with-meta (range 10) {:foo :bar}) is a linear-time operation

favila 2020-03-25T13:09:01.009500Z

(empty lazy-seq) returns a persistentlist

favila 2020-03-25T13:09:04.009800Z

I’m fine with that

favila 2020-03-25T13:09:07.010Z

that’s expected

2020-03-25T13:09:13.010300Z

that comment was about an alternate interpretation of your original question where you were only interested in preserving metadata when the arg was empty

favila 2020-03-25T13:09:22.010600Z

but what’s not expected is that it doesn’t have the metadata of the original lazyseq

2020-03-25T13:13:02.012600Z

I realize it's surprising, and that's unfortunate, I'm just pointing out why changing this behavior would be problematic in other ways

favila 2020-03-25T13:13:41.013Z

I didn’t catch that. why would it be problematic?

2020-03-25T13:16:38.013900Z

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}

2020-03-25T13:17:25.014300Z

in particular the last two steps, where (meta x1) is different from (meta (rest x1))

favila 2020-03-25T13:18:00.014500Z

yes, that is all fine

favila 2020-03-25T13:18:10.015100Z

I’m really more concerned with empty though

2020-03-25T13:18:20.015500Z

so what should (meta (empty x1)) do for that particular x1?

2020-03-25T13:18:33.016Z

should it give you the metadata that x1 has, or the metadata that x0 has?

favila 2020-03-25T13:18:46.016300Z

x1

alexmiller 2020-03-25T13:19:24.017100Z

I looked at the code and agree this is a bug

😂 1
favila 2020-03-25T13:19:35.017700Z

public IPersistentCollection empty(){
                                     PersistentList o PersistentList.EMPTY;
                                     return o.withMeta (o, meta ())
                                     }

favila 2020-03-25T13:19:43.018Z

is basically what I expect this to be

favila 2020-03-25T13:19:52.018400Z

(not tested, formatted weird because typed into a repl)

alexmiller 2020-03-25T13:19:55.018600Z

in general, most of the colls do retain metadata on empty

alexmiller 2020-03-25T13:20:17.019100Z

that seems to be missing on ASeq.empty() (which affects many subclasses) and LazySeq.empty()

alexmiller 2020-03-25T13:20:56.019600Z

some random subclasses do actually override and fix that, like ChunkedSeq.empty()

favila 2020-03-25T13:22:03.020700Z

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

alexmiller 2020-03-25T13:23:19.021500Z

in general, all collection operations are expected to retain metadata

alexmiller 2020-03-25T13:23:29.021800Z

(not all seq operations though)

alexmiller 2020-03-25T13:24:00.022400Z

empty is a collection operation though (and seqs are logical collections), so I think all that holds

👍 2
alexmiller 2020-03-25T13:26:13.022700Z

do you have jira access?

favila 2020-03-25T13:29:26.022900Z

yes

alexmiller 2020-03-25T13:29:49.023200Z

if you can file a ticket, that'd be great

favila 2020-03-25T13:40:35.023500Z

NP, thank you @alexmiller https://clojure.atlassian.net/browse/CLJ-2566

alexmiller 2020-03-25T13:40:45.023700Z

thx

seancorfield 2020-03-25T21:35:58.024500Z

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.

🎉 4
🙃 1