Skip to content
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

Closed
wants to merge 7 commits into from
Closed

RFC: EnumSet type #19470

wants to merge 7 commits into from

Conversation

simonbyrne
Copy link
Contributor

Similar to an Enum, but represents a set of values via bitwise operations. Types themselves support certain bitwise operations (&, | and xor), and corresponding set operations.


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
Copy link
Member

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

Copy link
Contributor Author

@simonbyrne simonbyrne Dec 1, 2016

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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.

@stevengj
Copy link
Member

stevengj commented Dec 1, 2016

+1 for the overall idea here. It's pretty common to have a bitwise-or of "flags", and it's nice to automate this.

@simonbyrne
Copy link
Contributor Author

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,
Copy link
Contributor

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?

Copy link
Contributor Author

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).

Copy link
Contributor

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.

Copy link
Contributor Author

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 |
Copy link
Contributor

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.

@mauro3
Copy link
Contributor

mauro3 commented Dec 2, 2016

Would be good to add some docs which elaborate when to use @enum vs @enumset. Would EnumFlags be a better name?

@simonbyrne
Copy link
Contributor Author

simonbyrne commented Dec 2, 2016

I agree, EnumFlag might be better choice.

@simonbyrne
Copy link
Contributor Author

Also, need to decide whether to make the abstract type parametric, like Enum (#18826)

@StefanKarpinski
Copy link
Member

I prefer EnumSet myself – it's a set of Enum values. To me a "flag" is a single value which can be included in a set of flags, so there isn't really a type that corresponds to the singular form – an "EnumFlag" is really just an EnumSet that happens to contain a single Enum value.

@StefanKarpinski
Copy link
Member

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...

@nalimilan
Copy link
Member

The name choice is indeed difficult. EnumSet evokes a Set containing enum values at first sight. Maybe FlagEnum?

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
Copy link
Contributor

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]
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

@waldyrious waldyrious Dec 3, 2016

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]
Copy link
Member

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

"""
@flagenum EnumName[::U] enumvalue1[=x] enumvalue1[=y]

Create an [`FlagEnum`](:obj:`FlagEnum`) type with name `EnumName` and base member values
Copy link
Contributor

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,
Copy link
Contributor

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

Copy link
Member

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
Copy link
Contributor

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

@simonbyrne simonbyrne mentioned this pull request Jan 3, 2017
42 tasks
@kshyatt
Copy link
Contributor

kshyatt commented Jan 15, 2017

What is the status on this?

@StefanKarpinski
Copy link
Member

I think we're stuck bikeshedding the name 😢

@mauro3
Copy link
Contributor

mauro3 commented May 5, 2017

Had the need for this today. How about a 0.7 on this, so this doesn't fall through the cracks? +1 for FlagEnum or something else containing "flag".

@StefanKarpinski StefanKarpinski added this to the 1.0 milestone May 5, 2017
@JeffBezanson
Copy link
Member

This should probably be a package, as should the existing Enum type. Enums only seem to be used by Distributed and LibGit2, both of which should also be packages.

@simonbyrne
Copy link
Contributor Author

Part of that reason is that it is due to (a) existing @enum doesn't support these sort of flags, and (b) it's loaded too late for a lot of things.

It could be useful in other places, e.g.

@StefanKarpinski
Copy link
Member

Let's go ahead with this and call it EnumSet; it can have an EnumSet(::Enum) constructor so that you can use single Enum values where an EnumSet is expected and get the set containing only that value.

@@ -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)
Copy link
Member

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?

@StefanKarpinski
Copy link
Member

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.

@StefanKarpinski
Copy link
Member

This is a feature and could be post-1.0; @JeffBezanson wants all of the things that use this to be removed from Base.

@StefanKarpinski StefanKarpinski modified the milestones: 1.x, 1.0 Aug 31, 2017
@simonbyrne
Copy link
Contributor Author

You want to remove Libdl.open?

@JeffBezanson
Copy link
Member

Yes, we can move Libdl out of Base.

@simonbyrne
Copy link
Contributor Author

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.

@JeffBezanson
Copy link
Member

+1 for EnumSet if we add this.

@vtjnash vtjnash added the triage This should be discussed on a triage call label Dec 13, 2017
@simonbyrne
Copy link
Contributor Author

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).

@vtjnash vtjnash assigned vtjnash and unassigned vtjnash Dec 14, 2017
@StefanKarpinski
Copy link
Member

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.

@StefanKarpinski StefanKarpinski removed the triage This should be discussed on a triage call label Dec 14, 2017
@DilumAluthge DilumAluthge removed this from the 1.x milestone Mar 13, 2022
@simonbyrne simonbyrne closed this Feb 13, 2023
@simonbyrne simonbyrne deleted the sb/enumset branch February 13, 2023 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.