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

Reply via email to