On 17/05/2016 18:47, Rasmus Schultz wrote:
I don't really understand what closures have to do with annotations, or how
that relates to capturing context.
The point is that, rather than trying to capture context, you can just
as well ask for what you need.
I'm going to use a very contrived example, because I really can't
think of any real case that requires context:
class User
{
<< new Validation(function ($value) { return false !==
filter_var($value, FILTER_VALIDATE_EMAIL); }) >>
public $email;
}
Like I said, extremely contrived - in practice, you wouldn't need to
attach functionality in that way.
Right, in my mind this is less about *context*, and more about *passing
code into the implementation*. I think we agree that an implementation
of validation-by-annotation would look something like:
$validators = $reflected_thing->getAnnotations('someFilter');
foreach ( $validators as $validator ) {
$validator->validate($some_instance);
}
This gets its *context* from the code running the validator; the
implementation of the validate() method would normally just be part of
the annotation type:
<<new RangeValidator(1, 100)>>
var $foo;
The closure example you gave would be a way of providing on-the-fly
dynamic *behaviour* as the argument to an annotation. This wouldn't
really make sense for validation (a custom validator would still
probably be defined once and used for several fields, but theoretically
you could have:
<<new CustomValidator(function($value){ return is_prime($value); })>>
It's not the context that's magic here, it's the ability to specify code
as part of the annotation *value*, rather than the annotation *definition*.
A more obvious example therefore is using annotations to implement DbC:
<<new Precondition(function($values) { ... })>>
Although as I admitted to Guilherme, that requires some magic to run it
at the right time anyway...
An example from the other thread of a context-bound annotation would be
implementing validation like this
You're not annotating the function - this is just a round-about way of
annotating the argument.
You would do that directly, like this:
function foo(
<< ValidateRange(0, 100) >>
int $percentage
) { ... }
Sure, but it's easy to come up with a variant that requires access to
more than one parameter, say:
<< ValidateLessThan($min, $max) >>
function foo($min, $max)
Which without any context or AST would need to be either:
<< ValidateLessThan('$min', '$max') >>
function foo($min, $max)
...which rather defeats the point of having code rather than just a string.
Or:
<< ValidateCustom(function($min, $max) { return $min < $max } >>
function foo($min, $max)
...which feels rather clunky.
If the expression would have to be made up entirely of constants anyway, might the same
"constant expressions" used in class const definitions be a better fit than "any
valid PHP expression" - plus a specific exception for creating objects out of those constant
expressions.
Probably not - what happens with what is today "nested annotations"
then? Or will you make an exception for those too?
How would you "nest" annotations in your scheme anyway? As far as I can
see, they would be either multi-level arrays (a valid constant
expression) or nested constructor calls. So there's still exactly one
exception made, the "new Foo(args)" construct.
The problem is, you're just reinventing a subset of the programming
language, and I'm sure you can keep expanding on that indefinitely.
What for? Just use the facilities already defined by the language.
That's a fair point. The flipside is that the more things you allow the
user to do, the more edge cases you have to deal with when evaluating
the expressions in your special "zero context".
This fear of any feature that lets you do "evil" is incomprehensible
to me. Most features of almost any programming language can be used
for "evil".
I am not suggesting removing the ability to avoid people doing evil; I'm
suggesting that the feature you call "simple annotations" could be a lot
simpler.
What is the value of being able to write this:
function foo() { return 'MyAnnotation'; }
<< foo() >>
class A {}
When you could just write this:
<< 'MyAnnotation' >>
IMO, the real question is whether a feature accomplishes
what you want. If you insist on something that also prevents things
you don't want, you're bound to end up with something a lot more
complex that fits into the language a lot less naturally...
In my mind, something that lets me evaluate an arbitrary expression in
some weird null-context, at an unspecified time (because I don't know
what code will be the first to request that annotation), only to have
the result stored in some invisible static variable, is *more* complex
than many of the alternatives.
Regards,
--
Rowan Collins
[IMSoP]
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php