Add @battery / @enumbattery: opt-in counterparts to @batteries#19
Conversation
|
I rebased this on #20 to get a green CI. So
before tackling #19. |
|
Awesome the opt in variant is a great idea. We definitely need that! #18 (comment) is a good argument in favor of the new syntax. I want to explore a few more ideas before deciding on the syntax question. For me very often I have I was wondering if we should support something like: Or we could make it really easy for the user to customize their own macro: macro mybatteries(args...)
defaults = (;StructHelpers.BATTERIES_DEFAULTS..., kwshow=true, kwconstructor=true, eq=false)
# defaults = (;StructHelpers.BATTERIES_NONE..., kwshow=true, kwconstructor=true)
return StructHelpers.batteriesmacro(args...; defaults)
endAnd then everything is decorated with |
This implements the first proposal in jw3126#19 (comment)
That's a fantastic idea! I am not sure that we need to do anything about custom macros. The user can already define their own macro which calls into |
|
This is very nice, thanks a lot for the contribution! |
|
Can you bump the version? |
Thanks, done |
This implements the first proposal in jw3126#19 (comment)
|
I am not yet sold that we should have the |
I am not exactly sure whether I understand this correctly. Do you mean #18 with " Note, that there is quite some precedence for this style, e.g. with Base.@assume_effects :nothrow :terminates_globally f(x) = ...(not using using Foo: a, b, cwhere you just list the bindings you want to have in your namespace similarly how we do. Or take #[derive(Debug, Clone, Hash, PartialEq, Eq)]also, no
"Big pain" is relative and might be a high bar: All this was assuming what you might have meant and I might be off. Do you think it might help to have a call? It would probably help me understanding your perspective. If you think this is helpful, I can send you a direct message via Julia's Discourse. |
|
Yes this is what I meant. My biggest concern is complexity creep for the reader. Now the reader needs to know about a new syntax special case. |
|
Ah, I guess this is a matter of perspective:
But anyway, you are the maintainer and we can use my branch where we have the feature. So I would rebase the PRs so that they are #19 -> #21 -> #18, correct? Then you can close #18 as "not planned" and we (and whoever else wants to avoid the redundancy) can use the branch from #18. |
|
I am always thinking of C++. Everybody got their favorite feature in and now there is tremendous complexity and foot guns. |
A bare symbol is now accepted as shorthand for \`flag=true\`:
@Batteries T kwconstructor # equivalent to kwconstructor=true
@Batteries T kwconstructor kwshow hash=false # mixable
Also unblocks the cleaner subset-mode call shape we want for an
upcoming \`@battery\` macro that derives a single hand-picked battery.
@Batteries derives a curated set of methods by default; @battery does the opposite — every default flips to false, so only the explicitly listed batteries are derived. The bare-flag shorthand from the previous PR makes call sites read as a checklist: @battery T kwconstructor # only the kwarg constructor @battery T eq isequal hash # only structural ==/isequal/hash @battery T hash typesalt=0xab # only stable hash This addresses the side-effect concern with subset use cases of @Batteries (\`@batteries T showrepr=true\` silently also defines ==, hash, isequal, getproperties, constructorof, selfconstructor): users who want exactly one or two batteries can now ask for just those, with no surprise additions now or additional surprises in the future when the defaults change. @enumbattery is the analogous opt-in form of @enumbatteries. Implementation factors the macro bodies into shared codegen helpers that take the base named tuple. The 'all-false' bases are derived programmatically from the existing DEFAULTS so they stay in sync if a new option is added later. Co-authored-by: Copilot <copilot@github.com>
This implements the first proposal in jw3126#19 (comment)
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Closes jw3126#12. This is the real PR I wanted to do. All the others are basically there to enable this one. Introduces a new `@batteries` / `@battery` flag `showrepr=true` that overloads `Base.show` to print a heuristically short constructor call which round-trips through `eval`, as an alternative to the existing `kwshow`. The two are mutually exclusive (both target `Base.show`). I originally wanted to just extend `kwshow`. However, as the redundancy reduction should also be available if there is no keyword constructor and/or calls are short when using a parametric constructor, this would have lead to broken backwards-compatibility requiring a major version bump and user changes. But as both approaches have different advantages, it's probably a good thing to have both in the future. Highlights: * Probes every constructor of the type — positional, keyword, and hybrid (including the pair synthesized by `Base.@kwdef`) — and picks the shortest call whose result is `repr_eq` to the original. Trailing fields whose values match a default are omitted. * Per-field literal shortening where the constructor still accepts the substitute: `0x000000000000002a` → `42`, whole-valued floats drop the `.0` suffix, `2//1` → `2`, `2 + 0im` → `2`. * Uniform / run-length-compressed vectors render as `fill(v, n)` (or `[v for _=1:n]` for non-`isbits` elements, to avoid aliasing on re-eval); heterogeneous vectors get per-run RLE only when it actually shortens the literal. * Round-trip safety is the gating criterion throughout: every substitution is verified by reconstructing through the constructor and comparing via `repr_eq` (which accepts both `==` and `isequal` to handle `NaN`, `-0.0`, and custom equality). * Falls back to a non-executable `T(field = value, …)` rendering when no constructor recreates the object. `kwshow` keeps its existing fixed `T(f1 = v1, …)` shape and stability guarantees; `showrepr`'s output is heuristic and not pinned across minor releases. The README contrasts the two. This builds on jw3126#18 and jw3126#19 which should be merged first. Co-authored-by: Copilot <copilot@github.com>
|
I think all conflicts are removed and we have everything on the branch where it belongs. Just give me a hint whether I should do further changes (comments too long? too many? testset names too long? …). Thanks a lot for giving this feature a place upstream. I really appreciate this. I totally agree that we want to avoid C++, but I think with this feature we are still a bit away from the dark side. :-) |
jw3126
left a comment
There was a problem hiding this comment.
Thanks a lot for implementing this. It is a nice improvement!
Closes jw3126#12. This is the real PR I wanted to do. All the others are basically there to enable this one. Introduces a new `@batteries` / `@battery` flag `showrepr=true` that overloads `Base.show` to print a heuristically short constructor call which round-trips through `eval`, as an alternative to the existing `kwshow`. The two are mutually exclusive (both target `Base.show`). I originally wanted to just extend `kwshow`. However, as the redundancy reduction should also be available if there is no keyword constructor and/or calls are short when using a parametric constructor, this would have lead to broken backwards-compatibility requiring a major version bump and user changes. But as both approaches have different advantages, it's probably a good thing to have both in the future. Highlights: * Probes every constructor of the type — positional, keyword, and hybrid (including the pair synthesized by `Base.@kwdef`) — and picks the shortest call whose result is `repr_eq` to the original. Trailing fields whose values match a default are omitted. * Per-field literal shortening where the constructor still accepts the substitute: `0x000000000000002a` → `42`, whole-valued floats drop the `.0` suffix, `2//1` → `2`, `2 + 0im` → `2`. * Uniform / run-length-compressed vectors render as `fill(v, n)` (or `[v for _=1:n]` for non-`isbits` elements, to avoid aliasing on re-eval); heterogeneous vectors get per-run RLE only when it actually shortens the literal. * Round-trip safety is the gating criterion throughout: every substitution is verified by reconstructing through the constructor and comparing via `repr_eq` (which accepts both `==` and `isequal` to handle `NaN`, `-0.0`, and custom equality). * Falls back to a non-executable `T(field = value, …)` rendering when no constructor recreates the object. `kwshow` keeps its existing fixed `T(f1 = v1, …)` shape and stability guarantees; `showrepr`'s output is heuristic and not pinned across minor releases. The README contrasts the two. This builds on jw3126#18 and jw3126#19 which should be merged first. Co-authored-by: Copilot <copilot@github.com>
Closes jw3126#12. This is the real PR I wanted to do. All the others are basically there to enable this one. Introduces a new `@batteries` / `@battery` flag `showrepr=true` that overloads `Base.show` to print a heuristically short constructor call which round-trips through `eval`, as an alternative to the existing `kwshow`. The two are mutually exclusive (both target `Base.show`). I originally wanted to just extend `kwshow`. However, as the redundancy reduction should also be available if there is no keyword constructor and/or calls are short when using a parametric constructor, this would have lead to broken backwards-compatibility requiring a major version bump and user changes. But as both approaches have different advantages, it's probably a good thing to have both in the future. Highlights: * Probes every constructor of the type — positional, keyword, and hybrid (including the pair synthesized by `Base.@kwdef`) — and picks the shortest call whose result is `repr_eq` to the original. Trailing fields whose values match a default are omitted. * Per-field literal shortening where the constructor still accepts the substitute: `0x000000000000002a` → `42`, whole-valued floats drop the `.0` suffix, `2//1` → `2`, `2 + 0im` → `2`. * Uniform / run-length-compressed vectors render as `fill(v, n)` (or `[v for _=1:n]` for non-`isbits` elements, to avoid aliasing on re-eval); heterogeneous vectors get per-run RLE only when it actually shortens the literal. * Round-trip safety is the gating criterion throughout: every substitution is verified by reconstructing through the constructor and comparing via `repr_eq` (which accepts both `==` and `isequal` to handle `NaN`, `-0.0`, and custom equality). * Falls back to a non-executable `T(field = value, …)` rendering when no constructor recreates the object. `kwshow` keeps its existing fixed `T(f1 = v1, …)` shape and stability guarantees; `showrepr`'s output is heuristic and not pinned across minor releases. The README contrasts the two. This builds on jw3126#18 and jw3126#19 which should be merged first. Co-authored-by: Copilot <copilot@github.com>
Closes jw3126#12. This is the real PR I wanted to do. All the others are basically there to enable this one. Introduces a new `@batteries` / `@battery` flag `showrepr=true` that overloads `Base.show` to print a heuristically short constructor call which round-trips through `eval`, as an alternative to the existing `kwshow`. The two are mutually exclusive (both target `Base.show`). I originally wanted to just extend `kwshow`. However, as the redundancy reduction should also be available if there is no keyword constructor and/or calls are short when using a parametric constructor, this would have lead to broken backwards-compatibility requiring a major version bump and user changes. But as both approaches have different advantages, it's probably a good thing to have both in the future. Highlights: * Probes every constructor of the type — positional, keyword, and hybrid (including the pair synthesized by `Base.@kwdef`) — and picks the shortest call whose result is `repr_eq` to the original. Trailing fields whose values match a default are omitted. * Per-field literal shortening where the constructor still accepts the substitute: `0x000000000000002a` → `42`, whole-valued floats drop the `.0` suffix, `2//1` → `2`, `2 + 0im` → `2`. * Uniform / run-length-compressed vectors render as `fill(v, n)` (or `[v for _=1:n]` for non-`isbits` elements, to avoid aliasing on re-eval); heterogeneous vectors get per-run RLE only when it actually shortens the literal. * Round-trip safety is the gating criterion throughout: every substitution is verified by reconstructing through the constructor and comparing via `repr_eq` (which accepts both `==` and `isequal` to handle `NaN`, `-0.0`, and custom equality). * Falls back to a non-executable `T(field = value, …)` rendering when no constructor recreates the object. `kwshow` keeps its existing fixed `T(f1 = v1, …)` shape and stability guarantees; `showrepr`'s output is heuristic and not pinned across minor releases. The README contrasts the two. This builds on jw3126#18 and jw3126#19 which should be merged first. Co-authored-by: Copilot <copilot@github.com>
@batteriesderives a curated set of methods by default;@batterydoesthe opposite — every default flips to false, so only the explicitly
listed batteries are derived. The bare-flag shorthand from #18
makes call sites read as a list:
This avoids side-effects with subset use cases of
@batteries(
@batteries T showrepr=truesilently also defines==,hash,isequal,getproperties,constructorof,selfconstructor):users who want exactly one or two batteries can now ask for just
those, with no surprise additions now or additional surprises
in the future when the defaults change.
@enumbatteryis the analogous opt-in form of@enumbatteries.Implementation factors the macro bodies into shared codegen helpers
that take the base named tuple. The 'all-false' bases are derived
programmatically from the existing
DEFAULTSso they stay in sync if anew option is added later.