-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: EnumSet type #19470
RFC: EnumSet type #19470
Conversation
|
||
Base.union{T<:EnumSet}(x::T, y::T) = x | y | ||
Base.intersect{T<:EnumSet}(x::T, y::T) = x & y | ||
Base.issubset{T<:EnumSet}(x::T, y::T) = x & y == x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would also be nice to be able to do EnumValue1 in x
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about that, but technically each "base member" is a singleton set, not an element. To make in
mathematically correct we would need another type to distinguish these, which didn't seem worthwhile. Or we could just fudge it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mathematically, one should probably think of this as mereology. https://en.m.wikipedia.org/wiki/Mereology
So it is possibly acceptable for in
to describe the mereologic relation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting: I like that idea.
I agree that in
is too useful not to have. How about we rename it to FlagEnum
, define in
to be what is currently issubset
, and get rid of all the set operations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought, issubset
has a nice infix operator too, and does perhaps match closer intuition.
+1 for the overall idea here. It's pretty common to have a bitwise-or of "flags", and it's nice to automate this. |
I've also added a demo of its use in the libgit2 bindings. Thoughts appreciated. |
const CHECKOUT_CONFLICT_STYLE_DIFF3 = Cuint(1 << 21) | ||
const CHECKOUT_DONT_REMOVE_EXISTING = Cuint(1 << 22) | ||
@enumset(CHECKOUT, | ||
CHECKOUT_SAFE = 1 << 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary to specify the values here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, as not all values are valid (e.g. 1 << 3
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I didn't see that some where left out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the ones further down are unnecessary, but part of me thinks it might be good practice to leave them in for reference to the headers.
pathspec::StrArrayStruct | ||
end | ||
StatusOptions(; show::Cint = Consts.STATUS_SHOW_INDEX_AND_WORKDIR, | ||
flags::Cuint = Consts.STATUS_OPT_INCLUDE_UNTRACKED | | ||
flags::Consts.STATUS_OPT = Consts.STATUS_OPT_INCLUDE_UNTRACKED | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indent on the following lines.
Would be good to add some docs which elaborate when to use |
I agree, |
Also, need to decide whether to make the abstract type parametric, like |
I prefer |
Oh, I see, you're kind of using it that way since the "flag" term is used for the declaration of the individual flags that can go into a corresponding "enum set". Hmm... |
The name choice is indeed difficult. |
be747da
to
cb15d61
Compare
Create an [`FlagEnum`](:obj:`FlagEnum`) type with name `EnumName` and base member values of | ||
`EnumValue1` and `EnumValue2`, based on the unsigned integer type `U` (`UInt32` by | ||
default). The optional assigned values of `x` and `y` must have exactly 1 bit set, and | ||
not overlap. `EnumName` can be used just like other types and enum member values as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather phrase this as "If the values for x and y are provided, they must each have a single bit on, and naturally, not coincide".
The change "set" --> "on" is to prevent confusion with the noun meaning of "set", and the change "overlap" --> "coincide" is because when only one bit is turned on, conceptually we wouldn't consider it an overlap (which evokes partial coincidence); it either coincides exactly, or doesn't.
|
||
|
||
""" | ||
@enumset EnumName[::U] EnumValue1[=x] EnumValue2[=y] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should change to @flagenum
, to match the rest of the code
regular values, such as | ||
|
||
```jldoctest | ||
julia> @enumset FRUITSET apple=1<<0 orange=1<<1 kiwi=1<<2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@flagenum
here as well, and maybe rename FRUITSET to something else (FRUITFLAGS?)
@@ -169,3 +169,39 @@ let b = IOBuffer() | |||
seekstart(b) | |||
@test deserialize(b) === apple | |||
end | |||
|
|||
|
|||
@flagenum VegetableSet carrot potato broccoli |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should VegetableSet
also be renamed, for consistency?
@@ -700,6 +700,22 @@ Types | |||
julia> f(apple) | |||
"I'm a FRUIT with value: 1" | |||
|
|||
.. function:: @flagenum FlagEnum[::U] EnumValue1[=x] EnumValue2[=y] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably not use camel case for instances. Use lower case or upper case? Cf. #19506
1df3f29
to
c39a6a2
Compare
""" | ||
@flagenum EnumName[::U] enumvalue1[=x] enumvalue1[=y] | ||
|
||
Create an [`FlagEnum`](:obj:`FlagEnum`) type with name `EnumName` and base member values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now should use @ref
syntax for cross-references, but there's no docstring for FlagEnum
@@ -1409,6 +1411,7 @@ export | |||
|
|||
@assert, | |||
@enum, | |||
@flagenum, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new exports need to be listed in a docs index to go into the new manual I believe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, should be added after stdlib/base.md:114
where @enum
's docstring is.
@@ -112,6 +112,7 @@ Base.typejoin | |||
Base.typeintersect | |||
Base.Val | |||
Base.Enums.@enum | |||
Base.Enums.@flagenum |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not this PR's fault, but this seems like it's in kind of an odd section
What is the status on this? |
I think we're stuck bikeshedding the name 😢 |
Had the need for this today. How about a 0.7 on this, so this doesn't fall through the cracks? +1 for |
This should probably be a package, as should the existing |
Part of that reason is that it is due to (a) existing It could be useful in other places, e.g. |
Let's go ahead with this and call it |
@@ -107,7 +107,7 @@ function issubset(l, r) | |||
end | |||
const ⊆ = issubset | |||
⊊(l::Set, r::Set) = <(l, r) | |||
⊈(l::Set, r::Set) = !⊆(l, r) | |||
⊈(l, r) = !⊆(l, r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the new type support issubset
?
Jeff just pointed out that my last comment about conversion is incoherent since general enums have overlapping bits. But still, this should be considered as a set type with individual flags embedded as singleton sets. |
This is a feature and could be post-1.0; @JeffBezanson wants all of the things that use this to be removed from Base. |
You want to remove |
Yes, we can move |
That was unhelpful, sorry. Actually after a bit more thought, I think we should move this out. We can always add the enum interfaces as extra. |
+1 for |
So, I spent 10 minutes trying to rebase, but came to the realisation that I don't actually like it as it currently stands. A lot of enums mix "flag" parts and set parts (e.g. the open mode enum). |
Ok, in that case we can leave things as they are and if, in some 1.x version we introduce a better version of this feature, we can introduce new APIs that use enum sets while keeping the old integer-based ones around as legacy APIs. Not the end of the world. |
Similar to an
Enum
, but represents a set of values via bitwise operations. Types themselves support certain bitwise operations (&
,|
andxor
), and corresponding set operations.