leiningen

N.B. The maintainers are on #leiningen on Libera chat IRC. Go there for direct support/bug reports.
ikitommi 2019-04-10T09:40:25.007800Z

wrote an issue about how leiningen merges maps - can’t remove values from those. Does anyone know/remember if there was a reason for this? or just a bug? https://github.com/technomancy/leiningen/issues/2556

2019-04-10T13:44:40.009200Z

@ikitommi I think I’ve seen nested meta supporting structures used instead for those cases before so the replace metadata worked.

2019-04-10T13:45:30.010800Z

But yeah, offhand I don’t know why the behavior is that way regarding nil. I guess sort of makes sense if that’s the merge semantic you were going for in some situations. Merging as combining instead of overwriting.

2019-04-10T13:45:42.011300Z

And lein mostly does combining merges of values

ikitommi 2019-04-10T14:03:43.016Z

I think it would be a small change to check if the key exists and value is nil, and it should override the value to nil. Like it works with non-collections already. There are at least three copies of meta-merge, would not want to make a fourth copy with just this change.

ikitommi 2019-04-10T14:05:49.018200Z

Using it in all projects, really like the way it allows the user be in charge how things are merged.

2019-04-10T15:37:14.019300Z

@ikitommi I agree it’d be nice to do what you want in some cases. I don’t think it auto handling nil that way makes sense though.

2019-04-10T15:38:05.020800Z

I think there are cases where you’d not want nil to wipe out other values. Instead “combine” them

2019-04-10T15:38:34.021600Z

Maybe when the merging values are non-collections it may be ok. But that may be a weird stipulation.

2019-04-10T15:39:19.022700Z

I often wish meta merging let you use a placeholder value to wrap a value when you needed to add metadata like :replace to something that doesn’t support it

2019-04-10T15:39:37.023100Z

Like some predefined box type record

2019-04-10T15:40:45.024400Z

{:a ^:replace #MergeWrapper {:value nil}}

ikitommi 2019-04-10T15:55:26.027500Z

My use case is with the #reitit routing library. I use meta-merge to accumulate route data from a route tree into endpoints. It works like a charm in all cases but in the one where I one want’s to set some default in the top-level and override (remove) that default somewhere along the way. The common case:

(def router
  (r/router
    ["/api" {:interceptors [::api]}
     ["/ping" ::ping]
     ["/admin" {:roles #{:admin}}
      ["/users" ::users]
      ["/db" {:interceptors [::db]
              :roles ^:replace #{:db-admin}}]]]))
, get’s expanded (and meta-merged) into:
(r/routes router)
; [["/api/ping" {:interceptors [::api]
;                :name :user/ping}]
;  ["/api/admin/users" {:interceptors [::api]
;                       :roles #{:admin}
;                       :name ::users} nil]
;  ["/api/admin/db" {:interceptors [::api ::db]
;                    :roles #{:db-admin}}]]

ikitommi 2019-04-10T15:58:27.030600Z

but adding a content-negotiation is done my setting a :muuntaja key with a instance (non-collection) as a value, preferaby to the root, effecting all routes. With the current meta-merge impl, it’s impossible to unset the value without some marker values like false etc. The thing that meta-merge handles explicit nil differently than the clojure.core/merge makes me think it’s a bug.

ikitommi 2019-04-10T16:00:32.032100Z

If this would be fixed in Leiningen, the change would be mirrored to the meta-merge library I’m using (https://github.com/weavejester/meta-merge). Another option is to make a friendly copy and name it somethin like ctrl-merge. There is already a bit-different duct-meta-merge around (https://github.com/duct-framework/core/blob/master/src/duct/core/merge.clj).

2019-04-10T16:03:16.033400Z

@ikitommi yeah, I understand you have a usage

2019-04-10T16:03:29.033800Z

but I’m saying the :replace metadata already exists for the concept of “replacing”/overwriting

2019-04-10T16:03:47.034300Z

the downfall to it is that it only works on things that support metadata - like not on nil right?

ikitommi 2019-04-10T16:03:59.034800Z

yes, doesn’t work with nil.

2019-04-10T16:04:04.035Z

So what I was saying

2019-04-10T16:04:23.035500Z

Was it’d be nice if the meta-merge transparently supported a “special wrapper type” that could take metadata for things that cannot have metadata

2019-04-10T16:04:34.035900Z

Then the same :replace mechanism could be used in any case - as well as :displace and whatever else

2019-04-10T16:04:54.036500Z

instead of hardcoding how something like nil is handled

ikitommi 2019-04-10T16:05:43.037900Z

hmm. I think the fix would be generic if it checked the map entry - if that exists and the value is not a collection, it would override.

2019-04-10T16:05:48.038200Z

Or maybe it’s all just about nil

2019-04-10T16:05:56.038600Z

And in that case, maybe top-level map could have metadata idk

ikitommi 2019-04-10T16:06:16.039Z

as it works already with all non-collections, like (meta-merge {:a 1} {:a 2}) ; => {:a 2})

2019-04-10T16:06:28.039500Z

Yeah, it’s possibly all about nil

2019-04-10T16:06:46.040Z

but you want nil to override, not sure all cases would want that - maybe you could justify as that’s just what non-collections do

2019-04-10T16:07:24.040400Z

I do wonder if things like lein take advantage of the current nil behavior though

2019-04-10T16:07:56.041400Z

There probably could be subtle issues changing it.

ikitommi 2019-04-10T16:07:58.041500Z

I was trying to find an example of that usage, but couldn’t find any..

2019-04-10T16:08:18.042Z

I think some cases may be hard to see

2019-04-10T16:08:27.042400Z

Profiles merging and having default keys added etc

ikitommi 2019-04-10T16:08:28.042500Z

yeah, tons of private repos etc.

2019-04-10T16:08:49.043Z

I don’t think it’s that uncommon for people to put :x nil in a map instead of not adding :x at all

2019-04-10T16:09:06.043500Z

in clj I find, you often have to go out of your way to not put the key in the map - it’s uglier

2019-04-10T16:09:24.044100Z

so in those cases, if that’s automatically happening, and it gets in front of a meta-merge - with your change, it’ll start wiping out other values instead of being ignored

ikitommi 2019-04-10T16:09:57.044600Z

so, too risky to change as something could break?

2019-04-10T16:10:31.045400Z

What I mean here: > in clj I find, you often have to go out of your way to not put the key in the map - it’s uglier (assoc m :x <don't know if I have this>) is commonly done, when really (cond-> m (some? <don't know if I have this>) (assoc :x <don't know if I have this>)) would be more appropriate

2019-04-10T16:11:20.045800Z

> so, too risky to change as something could break? I don’t really know. I’m just thinking it’d have to be looked into.

ikitommi 2019-04-10T16:15:47.049400Z

ok, thanks for giving it a thought. I understand if that can’t be changed, kinda fundamental and could break something - but there is now the issue of this.

alexmiller 2019-04-10T16:45:19.050Z

just another option, add meta-merge2 and users can migrate as needed

alexmiller 2019-04-10T16:45:25.050200Z

accretion, not breakage

2019-04-10T16:49:23.052Z

@alexmiller Yeah, definitely not a fan of using the same name for breaking things. I’m onboard with that concept. I’m not sure how the separate projects are kept in sync or whatever though - meta-merge vs lein.

2019-04-10T16:50:05.052600Z

I think it was already mentioned to copy/rename > If this would be fixed in Leiningen, the change would be mirrored to the meta-merge library I’m using (https://github.com/weavejester/meta-merge). Another option is to make a friendly copy and name it somethin like ctrl-merge. So that’s probably the meta-merge2 concept.

alexmiller 2019-04-10T16:50:23.053Z

yep

✅ 1
ikitommi 2019-04-10T20:08:26.055500Z

If Leiningen doesn't need the nil thing, them the version 2 should be issue of the meta-merge library, right?

ikitommi 2019-04-10T20:10:55.059300Z

If there should be a bug in the merge, effecting both meta-merge1 & 2, should the fix be applied in both? Any guidelines when to stop maintaining old versions (like soon: spec1)?

ikitommi 2019-04-10T20:12:04.061200Z

also, if a libraryX updates from using mm1 to mm2, should that lib also change the namespaces so that "no-one breaks"?

ikitommi 2019-04-10T20:16:11.064100Z

would be nice to have some written guidelines for preferred clojure accretion with libs, ping @alexmiller

alexmiller 2019-04-10T20:20:53.065700Z

There are not always simple answers to all of these questions. Rich’s Spec-ulation talk is still the best rendering of the problems

alexmiller 2019-04-10T20:21:33.066300Z

In general, turn breakage into accretion

alexmiller 2019-04-10T20:22:21.067300Z

Fixation should generally be considered non-breaking as a matter of definition

alexmiller 2019-04-10T20:22:49.068Z

But sometimes that is subtle if users rely on the “broken” behavior