Coder Social home page Coder Social logo

enum-comparison's People

Contributors

amalloy avatar atercattus avatar benmorel avatar crell avatar iluuu1994 avatar pbowyer avatar whisperity avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

enum-comparison's Issues

Do we really need case specific methods?

Sorry to raise this issue ^^ This has been the main criticism I've heard consistent across all platforms. As you know I have some mixed feeling on them. The benefits I can think of:

  1. Exhaustiveness safety at compile time when using abstract methods (or interfaces) on the base class
enum Suits {
	public function color();

	case Hearts {
		public function color() {}
	}

	case Diamonds; // Not handled, error
}
  1. It makes the state machine example possible

While I think the state machine example is cool it's clearly an edge case that can be achieved with a normal class hierarchy.

Do you have any other benefits to add?

Future scope? Combining multiple (non-value-associated) enum values

C# has the nice ability to do $permissions = Perm::READ | Perm::EXEC to describe a set of combinations of values; not every enum value is mutually exclusive.

Do we want to keep this possibility in mind while designing enums, or do we want to reject that entirely?

As I think about it right now, I feel like we need a proper EnumSet<? is Enum> generic for that so that we can distinguish these from simple values in our types.

Empty enum

The RFC currently says:

An Enumeration may have one or more case definitions, with no maximum, although at least one is required.

Any reason for requiring at least one case? All other language constructs allow empty bodies.

the type of enums and gettype() and is_enum()

What is Enums type? So far it is not said that Enums introduce a new type, so are they expected to be of type object?

If that's the case does it means that gettype() function's output would be object and there won't be a corresponding is_enum() function, but rather we should use the is_object() instead?

Don't you think it would be a little odd not to be able to distinguish between an object and enum value?

"enum class" keyword?

This compound keyword would eliminate the BC break of adding a new keyword. Although it looks a bit less pretty.

enum class Foo {
    case Bar;
}

At least something we should consider / talk about.

Manual instantiation of enum values

$class = get_class(SomeEnum::SomeValue);
$obj = new $class;

What is $obj now? Does that throw?
What about new ReflectionClass(SomeEnum::SomeValue)->newInstanceWithoutConstructor();?
What about $obj = unserialize(serialize(SomeEnum::SomeValue));

Esp. the latter case should work I think, and return the singleton object.

var_export()

Right now, it produces a __set_state-based output that populates a class. That's almost certainly wrong.

Instead, probably just produce the FQCN of the case so that = works correctly.

Do we need Enum::Case type?

$a instanceof Suit::Spades; // true

I'm not entirely sure we really need support for the type Enum::Case at this point because it's equivalent to ===. It's only potentially useful when you want to call methods in some other context without checking/asserting the case first.

Static methods

The RFC says:

Specifically, the following features of objects are not allowed on enumerations: Static methods

Any specific reason this shouldn't be allowed? Also, are we talking about enums or the enum cases?

Example where this would be useful:

enum Foo {
    ...

    public static function fromRepresentation1($value) {
        return match ($value) { ... };
    }

    public static function fromRepresentation2($value) {
        return match ($value) { ... };
    }
}

$foo = Foo::fromRepresentation1(1);
$foo = Foo::fromRepresentation2('foo');

Are constants allowed?

We permit static methods … do we also permit constants (on enum decls, anyway not on cases; should be stated explicitly in the RFC though)? Should be symmetrical, but not sure about possible confusions.
And in the same thought train, static properties?

Allow constants

It has been mentioned multiple times that allowing constants in enums would be useful for creating aliases to avoid BC breaks.

enum Foo {
    case Bar;
    #[Deprecated]
    const Baz = self::Bar;
}

TARGET_ENUM/TARGET_CASE

Like with reflection I don't think we should differentiate between TARGET_CLASS and TARGET_ENUM/TARGET_CASE. For example, we don't do that for interfaces/traits either.

Allow implementing interfaces in enum cases

Enum Cases may not implement interfaces themselves.

Since we allow implementing methods that are only available in a subset of the cases allowing to implement interfaces only for a subset of cases would probably also make sense.

dynamic "match type"

What are accepted values on the LHS of =>? Is that supposed to work like instanceof, e.g. match type ($foo) { $enumclassname1 => ...,$enumclassname2 => ..., } would be valid? And generally applicable to all objects / types (e.g. int, stdClass etc.)

Clarify types of operations that are allowed in expressions in RFC document/implementation

https://wiki.php.net/rfc/enumerations currently says

Scalar equivalent values must be literals. Constants and constant expressions are not supported.

This was relaxed to allow -1 - -1 is not a literal

        0 => AST_UNARY_OP [UNARY_MINUS] #1
                expr => 1

The representation of PHP_INT_MIN is not a literal (9223372036854775808 is a float, and the negation of a float is a float)

php > var_export(PHP_INT_MIN);
-9223372036854775807-1

Related to #56

Type coercion?

Do we really need type coercion? Especially since the RFC currently only allows one-sided coercion (from objects to scalars) I'd be happier dropping it altogether.

EnumType::values()

Do we auto-generate an EnumType::values() method when the enum has only Unit Cases?

Pro: It's a common-enough use case. It's probably not too hard to do.

Con: Possibly interesting error handling when an Enum Type has Associable Cases, since then we can't have the method. Or the method has to throw an exception. Or something. Is the order locked at Lexical order or do we leave it undefined? What exactly is returned, strings or objects?

Discuss.

var_dump

We should change var_dump output to enum(Foo::Bar). I'm not sure if this needs to be specified in the RFC.

No semi-colon after case with body

From the RFC:

enum Suit implements Colorful {
  case Hearts {
    public function color(): string {
      return "Red";
    }
  };  // Note the semi-colon here!
}

I think there should be no semi-colon here. This way we're consistent with methods with or with no body.

abstract class Foo {
    function bar();
    function baz() {}; // Syntax error, unexpected token ";"
}

https://3v4l.org/Y0Fe1

Thoughts?

Reflection

The enum currently proposes two new reflection types: ReflectionEnum and ReflectionCase.

There's a very very large overlap with ReflectionClass for both of these since they are in fact just classes. Thus I suggest also actually making them subclasses of ReflectionClass.

  • ReflectionEnum::[hasCase|getCases|getCase]
    • These are in fact practically equivalent to hasConstant|getConstants|getConstant
    • Thus, I'm not sure we need/want them
  • ReflectionCase::getEnum
    • This is exactly equivalent to getParentClass()

Note that interfaces and traits also don't have separate reflection classes since they are so similar to classes. I propose doing the same here.

Serialization

The following object functionality is available, and behaves just as it does on any other object:

  • ...
  • __get, __call, __serialize, __deserialize, and __invoke magic methods

Does it actually make sense to __serialize and __deserialize? I see no point in this, you're not allowed to store properties on the enum anyway.

Extending enums

Will it be possible to write enum A extends B {} where B is another enum? - Yes, I see no fundamental reason why that should not work out.
Can we use traits in enums and cases? - Yes, I would say so.

Float to string in cases() assoc array

If the enumeration has a primitive equivalent, the keys will be the corresponding primitive for each enumeration. If the enumeration is of type float, the keys will be rendered as strings. (So a primitive equivalent of 1.5 will result in a key of “1.5”.)

I'm afraid this could result in some rounding errors.

https://3v4l.org/ng9vE

Add value() method

For scalar-backed enums, add a value() method that returns the corresponding value.

For unit enums, the method doesn't exist and so will error out like any other missing method. Possibly with a more targeted error message.

Rename ScalarEnum to Raw- or ValueEnum?

Based on the discussion in R11.

Scalar IMO is not a very descriptive name. 1. Not all scalars are allowed 2. If we ever allow non-scalars (like enums) then the term will also be wrong.

Since our property is called value ValueEnum might make sense although I think that will become confusing as soon as we have ADTs. I'd prefer RawEnum (also renaming value to raw).

@Crell WDYT?

Swift Optional enum

It also becomes a natural and easy way to implement Monads in user space, and both Haskell and Rust do exactly that in their core libraries. (I'm not clear if Swift does, but you can absolutely do so yourself).

Swifts Optional type is also just an enum.

@frozen enum Optional<Wrapped> 

They just have fancy syntax that means the same thing (e.g. Int? => Optional<Int>).

SqlObjectStorage over WeakMap

Comment from Nikita:

Rather than WeakMap, the possibly more natural choice for using enum keys
is SplObjectStorage. Of course, SplObjectStorage, like anything that is
part of SPL, has some peculiarities... Of course, just allowing them as
array keys would be ideal, but I agree that this should not be covered by
this RFC. This is something I may look into.

I agree with Nikita. SplObjectStorage seems more fitting as WeakMap is specifically designed for avoiding reference counting. It's not really relevant for constants since they will not be released until the end of the script but SplObjectStorage still seems like the more obvious choice.

Serialization

Right now, serialization is either a loophole around === working, or a fatal. Unless we can be certain how it's going to behave, we may want to just forbid it for now, at least until we figure out how it should work better.

I don't think unserialize(serialize(Suit::Clubs)); is ever going to really play nice without a ton of work, so better to not have it half-work.

Protected is not identical to private

Public, private, and protected methods. (Protected methods are effectively identical to private as inheritance is not allowed.)

This is true only for enum cases but not the enums themselves.

enum Foo {
    case Bar {
        public function baz() {
            $this->qux(); // Error
        }
    }

    private function qux() {}
}

Points about enumerations in Scala

README.md says

I deliberately excluded languages with no native enum support. Languages such as Javascript, Go, or Ruby do not (as far as I can tell) have any native enumerations, although there are various hacky ways to simulate them in user space. That is not of interest to us at this time.

Scala 3, due to be released in a few months, will in fact have language support. http://dotty.epfl.ch/docs/reference/enums/enums.html

However Scala 2.x does not. Enumeration is a class in the standard library that uses reflection and takes advantage of several Scala language features to make it easy to define simple enumerations.

People often use sealed traits + case objects instead (which is basically how ADTs are done in Scala, and as linked above in Scala 3 enum/ADT support desugars to that.)

A popular approach is to use https://github.com/lloydmeta/enumeratum#usage, which uses Scala's compile-time metaprogramming vs. runtime reflection to provide enum support as a library.

Again, Scala 3 builds on that approach of using sealed trait + case objects / case classes, which is how ADTs are modeled as well, and provides first-class syntax for enums and ADTs. However enums can be compiled as java.lang.Enums.

So Scala really bridges your distinction of "Fancy Object" languages and "ADT" languages. ADTs are objects and classes that can be safely pattern matched. In Scala the syntax encourages you to think of classes as parameterized objects. And the new enum feature in Scala 3 generalizes over ADTs and enums. When some cases are parameterized you simply lose the capabilities that no longer make sense (like getting a list of all values) and keep everything else.

Attributes on enums

Quoting example from rightfold: https://chat.stackoverflow.com/transcript/message/50502258#50502258

enum Suit {
    use EnumString, EnumInt; // these traits use the AsString and AsInt attributes
    #[AsString('H'), AsInt(1)] case Hearts;
    #[AsString('C'), AsInt(2)] case Clubs;
    #[AsString('D'), AsInt(3)] case Diamonds;
    #[AsString('S'), AsInt(4)] case Spades;
}

I think that's a good usage of attributes - and having a good usage available, we should probably allow them.

Pattern matching design considerations

I think it's sort of okay to bring the pattern matching as a separate RFC, but a well thought out design for pattern matching should exist beforehand, so that they can be nicely integrated at some future point without having to realize fundamental shortcomings in enums with associated values later on.

I wish to see couple a solid examples of it and why they would work out.

visibility on associated values

Is there any case or reason why an enum should ever contain protected or private contents?
I consider an enum to be a fully transparent object without hidden state - immutable and fully open. Enums are data, not state.

As such I would also drop the visibility modifier from associated values argument lists.

Traits

WTF, traits. 😄

How easy is it to only allow traits that have no properties? We want to avoid those becoming a back-door way to introduce state.

Swift example

case Suit {
    case hearts(String)
    case diamonds(String)
    case clubs(String)
    case spades(String)
}

I get that every language uses the same example for better comparison. It would be easier to demonstrate that each case holds its own set of associated values with a different example where each case holds different arity and type of assoc values.

Interaction with the object typehint

How do enum interact with the object typehint ? Being backed by objects, a naive implementation would probably allow enums to satisfy such a typehint. But that might be counter-intuitive for devs not knowing the internal implementation.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.