Re: [PHP-DEV] Static (factory) methods in attributes and initializers

2021-10-04 Thread Nikita Popov
On Mon, Sep 27, 2021 at 5:58 PM Andreas Hennings 
wrote:

> Hello list,
>
> currently, the default mode for attributes is to create a new class.
> For general initializers, with
> https://wiki.php.net/rfc/new_in_initializers we get the option to call
> 'new C()' for parameter default values, attribute arguments, etc.
>
> Personally I find class construction to be limiting, I often like to
> be able to use static factories instead.
> This allows:
> - Alternative "constructors" for the same class.
> - A single constructor can conditionally instantiate different classes.
> - Swap out the class being returned, without changing the factory name
> and signature.
>
> In fact, static factories for initializers were already mentioned in
> "Future Scope" in https://wiki.php.net/rfc/new_in_initializers.
> However this does not mention static factories for the main attribute
> object.
>
> For general initializers this is quite straightforward.
> For attributes, we could do this?
>
> // Implicitly call new C():
> #[C()]
> # Call the static factory instead:
> #[C::create()]
>
> So the only difference here would be that in the "traditional" case we
> omit the "new " part.
>
> We probably want to guarantee that attributes are always objects.
> We can only evaluate this when somebody calls ->newInstance(), because
> before that we don't want to autoload the class with the factory. So
> we could throw an exception if the return value is something other
> than an object.
> I was also considering to require an explicit return type hint on the
> factory method, but again this can only be evaluated when somebody
> calls ->newInstance(), so the benefit of that would be limited.
>
> The #[Attribute] annotation would allow methods as target.
>
> Reflection:
>
> ::getArguments() -> same as before.
> ::getName() -> returns "$class_qcn::$method_name".
> ::getTarget() -> same as before.
> ::isRepeated() -> This is poorly documented on php.net, but it seems
> to just look for other attributes with the same ->getName(). So it
> could do the same here.
> ::newInstance() -> calls the method. Throws an error if return value
> is non-object.
>
> we could add more methods like ReflectionAttribute::isClass() or
> ReflectionAttribute::isMethod(), or a more generic ::getType(), but
> these are not absolutely required.
>
> We could also allow regular functions, but this would cause ambiguity
> if a class and a function have the same name. Also, functions cannot
> be autoloaded, so the benefit would be small. I'd rather stick to just
> methods.
>

I see where you're coming from here, and I think an argument could be made
that our attribute syntax should have been #[new C()], allowing us to
associate arbitrary values -- which would naturally allow the use of static
factory methods once/if they are supported in constant expressions.

As that particular ship has sailed, I'm not convinced that supporting
static factory methods as "attributes" would be worthwhile. It's a
significant complication to the system (that users need to be aware of, and
consumers of the reflection API need to handle) for what ultimately seems
like a personal style choice to me. Do you have any examples where using
static factories over constructors for attributes would be particularly
compelling?

Regards,
Nikita


Re: [PHP-DEV] Static (factory) methods in attributes and initializers

2021-09-27 Thread Andreas Hennings
Hello Levi,
I thought it was clear.
By static "factory" method I just mean any static method that returns an object.
Don't interpret too much into the term :)

So to clarify, here is an example:

class OtherAttribute {}

class MyAttribute {
  public function __construct(string $name) {..}

  #[Attribute]
  public static function create(string $name): self {
return new self($name);
  }

  #[Attribute]
  public static function createOther($x): OtherAttribute {
return new OtherAttribute($x);
  }
}

class SomeValue {
  public static function create() {return new self();}
}

#[MyAttribute::create('hello')]
#[MyAttribute::createOther(SomeValue::create())]
class C {}

// This is the other part, static method calls in regular initializers.
function f($x = SomeValue::create())


Things to observe:
- The classes MyAttribute and OtherAttribute are not marked as
attribute via #[Attribute] annotation. Only the static methods are.
- MyAttribute::create() and MyAttribute::createOther() are allowed to
return any object that they want.

On Mon, 27 Sept 2021 at 18:26, Levi Morrison via internals
 wrote:
>
> I'm not exactly sure what you are proposing. However, it's always
> worth noting other languages which have something in the feature
> space, and Dart has both named constructors and factory constructors.
> You should start with researching what they do, and what other
> languages may have the same as _language features_, and not as
> patterns e.g. Java has a factory pattern, but it's not part of the
> language (at least not that I last checked, I don't follow Java).
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Static (factory) methods in attributes and initializers

2021-09-27 Thread Levi Morrison via internals
I'm not exactly sure what you are proposing. However, it's always
worth noting other languages which have something in the feature
space, and Dart has both named constructors and factory constructors.
You should start with researching what they do, and what other
languages may have the same as _language features_, and not as
patterns e.g. Java has a factory pattern, but it's not part of the
language (at least not that I last checked, I don't follow Java).

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Static (factory) methods in attributes and initializers

2021-09-27 Thread Andreas Hennings
Hello list,

currently, the default mode for attributes is to create a new class.
For general initializers, with
https://wiki.php.net/rfc/new_in_initializers we get the option to call
'new C()' for parameter default values, attribute arguments, etc.

Personally I find class construction to be limiting, I often like to
be able to use static factories instead.
This allows:
- Alternative "constructors" for the same class.
- A single constructor can conditionally instantiate different classes.
- Swap out the class being returned, without changing the factory name
and signature.

In fact, static factories for initializers were already mentioned in
"Future Scope" in https://wiki.php.net/rfc/new_in_initializers.
However this does not mention static factories for the main attribute object.

For general initializers this is quite straightforward.
For attributes, we could do this?

// Implicitly call new C():
#[C()]
# Call the static factory instead:
#[C::create()]

So the only difference here would be that in the "traditional" case we
omit the "new " part.

We probably want to guarantee that attributes are always objects.
We can only evaluate this when somebody calls ->newInstance(), because
before that we don't want to autoload the class with the factory. So
we could throw an exception if the return value is something other
than an object.
I was also considering to require an explicit return type hint on the
factory method, but again this can only be evaluated when somebody
calls ->newInstance(), so the benefit of that would be limited.

The #[Attribute] annotation would allow methods as target.

Reflection:

::getArguments() -> same as before.
::getName() -> returns "$class_qcn::$method_name".
::getTarget() -> same as before.
::isRepeated() -> This is poorly documented on php.net, but it seems
to just look for other attributes with the same ->getName(). So it
could do the same here.
::newInstance() -> calls the method. Throws an error if return value
is non-object.

we could add more methods like ReflectionAttribute::isClass() or
ReflectionAttribute::isMethod(), or a more generic ::getType(), but
these are not absolutely required.

We could also allow regular functions, but this would cause ambiguity
if a class and a function have the same name. Also, functions cannot
be autoloaded, so the benefit would be small. I'd rather stick to just
methods.

-

Side note: I think "attributes" is a really poor name for findability.
But this ship has sailed.

-

Cheers
Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php