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

Introduce procedural / functional @implements tag.. #1689

Open
donquixote opened this issue Dec 3, 2015 · 29 comments
Open

Introduce procedural / functional @implements tag.. #1689

donquixote opened this issue Dec 3, 2015 · 29 comments

Comments

@donquixote
Copy link

This suggestion is based on an observation in Drupal 7.
It is becoming less of a problem with more and more code being OOP (e.g. moving to Drupal 8), but it might still apply to some people or more, for some time or longer.

Problem: The framework (or CMS) forces a specific parameter signature for functions with a specific role.
In Drupal 7 these are hooks, theme functions, form builder functions, etc.

The IDE does not know this, and complains about unused parameters.

For a class method overriding an interface method, it does not care if parameters are unused, because it understands that the parameters are required. But for procedural, this does not work.

The official way to document this in Drupal 7 is "Implements hook_foo()". But this is too much of a Drupalism to suggest support by the IDE or phpDocumentor. Also, it only applies to hooks, not to theme functions or form callbacks.

So I am going to suggest an @implements doc tag for functions, methods and closures - equivalent to the native "implements" statement, but on function level.

The @implements tag would indicate that the function has the same (or weaker) signature as the one it implements, in the same way that a class method must have the same (or weaker) signature as the interface method it implements.

An IDE that understands this could

  • Complain about signature mismatch.
  • Stop complaining about unused parameters in the implementing function.

This is how it would look like:

function hook_eat(Food $food) {}

/**
 * @implements hook_eat()
 */
function my_eat(Food $food) {
  // No need to actually use the $food parameter.
}

/**
 * @implements hook_eat()
 */
$f = function(Food $foo) {
}

class C {
  /**
   * @implements hook_eat()
   */
  static function eatQuietly(Food $foo) {}
}

Old discussion on drupal.org: https://www.drupal.org/node/983268 (I am not promising anything about whether the Drupal community will pick this up)

Doxygen has something like it, but I don't quite understand how it is meant to be used..
http://www.stack.nl/~dimitri/doxygen/manual/commands.html#cmdimplements


EDIT: I just realize there is a github user with this name :)
Code formatting of @implements helps.

@mvriel mvriel closed this as completed Dec 6, 2015
@mvriel mvriel reopened this Dec 6, 2015
@mvriel
Copy link
Member

mvriel commented Dec 6, 2015

I just wrote a topic on discussing things on the PHP-FIG mailing list but this is the phpDocumentor issue tracker itself; which is home to experimental stuff ;)

@mvriel
Copy link
Member

mvriel commented Dec 6, 2015

I invite anyone here to comment on this issue but I am interested in the opinion of other FIG members before diving too deep into this request right now

@sun
Copy link

sun commented Dec 6, 2015

I don't speak for them, but I guess WordPress should be interested in this, since most of the application's architecture is based on hooks. Due to their backwards-compatibility policy, that's most probably going to stay for many years to come.

There are probably several other legacy systems with similar architectures that could benefit from @implements, so 👍 for setting up a simple standard way for documenting these kind of procedural function relationships.

@LionsAd
Copy link

LionsAd commented Dec 14, 2015

+1 to @implements

@LionsAd
Copy link

LionsAd commented Dec 14, 2015

Reading the discussion on PHP-FIG: https://groups.google.com/forum/?fromgroups=#!topic/php-fig/49ZfuJ1iisg

I think the best would be to use @signature instead of @implements

Or @implements_signature

or @function_signature

or @method_signature

or something like that

@implements indeed could be mixed with interfaces and especially {@inheritdoc}.

@Fleshgrinder
Copy link

Please have a look at the linked issue as well. The @implements tag could make sense in situations where we actually want to refer to an interface; e.g. traits that implement interfaces.

@donquixote
Copy link
Author

I like @signature. It seems unfamiliar as a docblock tag, but the meaning is 1:1 what we want.
I do not trust my own memory here, but at least https://en.wikipedia.org/wiki/Type_signature says that it specifies parameters, return type and errors (exceptions), so I assume this is the most common understanding of the term. It is interesting to see how other languages do it.

The other option I see is using something that works with @type/@var and @param, and that we still have to specify in more detail.
This is less intuitive at first, but the advantage would be that it can also be used on variables.

function fooProto(X $x) {}

/**
 * @param X $x
 *
 * @ŧype function:fooProto()
 */
function fooImpl(X $x) {}

/**
 * @var function:fooProto()
 */
$callback = 'fooImpl';

/**
 * @param function:fooProto() $callback
 * @param X $x
 */
function callFoo($callback, X $x) {
  $callback($x);
}

Otherwise we would have different constructs for callback variables and the actual function declaration.

@donquixote
Copy link
Author

Other interesting questions:

  1. Is there some kind of inheritance?
    E.g. with interfaces, the parameter signature of a method can be equal or weaker / more permissive than its parent, and the return type and @throws can be equal or more committed / specific.
  2. Are two methods with the same parameters, return type and @throws, but with different @signature tags, considered to be compatible, or not?

Example for the second question:

function fooProto(X $x);
function barProto(X $x);

/**
 * @param function:fooProto $callback
 * @param X $x
 */
function callFoo($callback, X $x) {
  callBar($callback, $x);
}

/**
 * @param function:barProto $callback
 * @param X $x
 */
function callBar($callback, X $x) {
  $callback($x);
}

@Fleshgrinder
Copy link

Note that the specification of a function/method is already possible with the @param tag since its definition states that any FQSEN is supported. All of the following are valid constructs:

/**
 * @param \global_function $p
 * @param \GLOBAL_CONSTANT $p
 * @param \$global_variable $p
 * @param \GlobalClass $p
 * @param \Name\Spaced\function $p
 * @param \Name\Spaced\CONSTANT $p
 * @param \Name\Spaced\Class::$property $p
 * @param \Name\Spaced\Class::function $p
 * @param \Name\Spaced\Class $p
 */

Note that you could include brackets for the functions if you like. All in all, there is no need for the function prefix.

The problem with the @type tag is that it was/is already in use as an alias for @var by some. This would mean that we give it a new meaning and create problems for some users and tools.

I am still in favor for the @implements tag and extend its scope from traits (as proposed in the other issue of mine) to any structural element. However, I understand that there might be confusion and that the name ties it to the interface data type. After giving it some though I propose @provides. This could be used for any kind of structural element, makes sense in English sentences, and expresses best what the structural element does.

Logger trait provides logger interface.

/**
 * @provides \Psr\Log\LoggerInterface
 */
trait LoggerTrait {}

Implemented function provides prototype's function signature.

function fooProto(X $x) {}

/**
 * @provides \fooProto
 */
function fooImpl(X $x) {}

Callback provides prototype's function signature.

function fooProto(X $x) {}

/**
 * @provides \fooProto
 */
$cb = function (X $x) {}

@donquixote That is why I think that the things are related, we can reuse and combine it. 😊

@donquixote
Copy link
Author

@Fleshgrinder

Note that the specification of a function/method is already possible with the @param tag since its definition states that any FQSEN is supported.

This may be true, but so far I don't think there is a common agreement what it means to use a function FQSEN as a @param type specifier. If we want to agree that this can specifies a callback type, fine.

The problem with the @type tag is that it was/is already in use as an alias for @var by some. This would mean that we give it a new meaning and create problems for some users and tools.

Ok. So we could allow function signature type specifiers in @type / @var on callback variables, but we would need a different tag for function/method declarations, to avoid confusing some tools.

I am still in favor for the @implements tag and extend its scope from traits

I am ok with this, but regard it as independent from this discussion.

However, I understand that there might be confusion and that the name ties it to the interface data type.

I understand the confusion (as stated before), although to me it simply means we would be bold and extend the meaning to a new domain. This would be a creative and autonomous action, but I don't see it creating a real conflict - except what I said in my previous comment about inheritance and such.

After giving it some though I propose @provides. This could be used for any kind of structural element, makes sense in English sentences, and expresses best what the structural element does.

I am not very convinced of this term. To me, "provide" is a fuzzy word, and I somehow associate it with "give". E.g. @provides int to me sounds similar to @return int.

/**
 * @provides \fooProto
 */
$cb = function (X $x) {}

I think here I would work with @type / @var, because a closure IS a variable.

@donquixote
Copy link
Author

Other interesting questions:

  1. Is there some kind of inheritance?
    E.g. with interfaces, the parameter signature of a method can be equal or weaker / more permissive than its parent, and the return type and @throws can be equal or more committed / specific.

Here it gets funny.
In regular use of the terms, an interface can "extend" another interface, a class can "extend" another class, but only a class can "implement" an interface.
On the other hand, if we use this for functions, a function can "implement" another function, which can "implement" yet another function.

  1. Are two methods with the same parameters, return type and @throws, but with different @signature tags, considered to be compatible, or not?

(see code example 3 posts before)
To me, the term "signature" only describes the parameter types, return values and such, so @signature fooProto() would be equivalent with @signature barProto().
On the other hand, "implements" to me implies a semantic meaning beyond just the raw signature. So @implements fooProto() to me would NOT be equivalent/interchangeable with @implements barProto().
@implements fooProto() would be imply @signature fooProto(), but not in the other direction.

@mvriel
Copy link
Member

mvriel commented Dec 26, 2015

Ummm.. @param does not support aan fqsen. It has a Type as first argument

@Fleshgrinder
Copy link

@mvriel oops you are of course right, it does not support FQSEN. Would it be a problem though? On the other hand, allowing it also creates confusion:

/**
 * @param \Name\Spaced\Class::function $cb
 */
function fn(callable $cb): string {}

Well, what is correct now? How do we verify (statically) that things are as they should be (easy and fast)? I don't know if this is feasible or even possible. Maybe you can shed some light on this matter. Note that the function prefix within @param has the same problem. However, we should think about FQSEN support and other reusable and well known syntax before introducing new kinds of prefixes; e.g. we could use generic syntax:

/**
 * @param callable<\Name\Spaced\Class::function> $cb
 */
function fn(callable $cb): string {}

But I dislike this version compared to the previous one because we repeat callable and did not really solve the problem.

I understand the confusion (as stated before), although to me it simply means we would be bold and extend the meaning to a new domain. This would be a creative and autonomous action, but I don't see it creating a real conflict - except what I said in my previous comment about inheritance and such.

Actually I fully agree with you that it does not create confusion. For me the things with implements is crystal clear because it depends on context, like the extends keyword does.

I am not very convinced of this term. To me, "provide" is a fuzzy word, and I somehow associate it with "give". E.g. @provides int to me sounds similar to @return int.

I do not share this association but it seems like a bad keyword if it could create such confusion.

I think here I would work with @type / @var, because a closure IS a variable.

You are right and I agree that the @var would be sufficient. However, it has the same problem as described before and we would need to either support FQSEN (and IDEs would need to check on their own what kind it is) or something else that tells the full story. Btw. we could also say that functions actually require the brackets. But that would be different to how FQSENs are currently parsed through most tools and IDEs.

@donquixote
Copy link
Author

@Fleshgrinder

Well, what is correct now? How do we verify (statically) that things are as they should be (easy and fast)? I don't know if this is feasible or even possible.

Well, first of all, "class" and "function" are reserved words, so i am going to rename them in the example.. Also, I like to add the "()" brackets to the type specifier. So, your modified code sample with some extra stuff:

/**
 * @param \N\C::fooProto() $f
 * @param \Animal\Duck[] $ducks
 */
function fn(callable $f, array $ducks): string {}

For both parameters, the PHP type hint is different from the doc type specifier. For both parameters, the doc type specifier is more specific than the PHP type hint, because phpDocumentor has a higher expressiveness than PHP itself. And in both cases, when analysing, the more expressive version wins.

Or where do you see the problem?

Btw. we could also say that functions actually require the brackets.

Well.. I prefer the brackets, makes it more obvious to everyone on first sight.
Without the brackets, your example with would still be clear. What else can \N\C::fooProto be, if not a method?
On the other hand, a procedural function and a class with the same name can coexist. So yeah, this would be a reason to require the brackets.

But that would be different to how FQSENs are currently parsed through most tools and IDEs.

Currently, my IDE (PhpStorm, who would have guessed) does not support functions in @param, afaik, or at least it does not do anything useful with it.
It does support them in @see, though, and there the brackets allow it to distinguish class vs function.

And if not: What is the worst that could happen, really?
Any decent docblock inspection tool should be robust enough to survive some unexpected and "confusing" stuff.

@mvriel, @Fleshgrinder

Ummm.. @param does not support aan fqsen. It has a Type as first argument

Imo, we should forget about this "everything is an fqsen". In @param, we could say "everything is a type", and then we simply need to say that a callback signature specifier IS a type. Yes, it should be specified with the fully qualified namespace, but I don't care if the complete thing including the brackets is considered an fqsen. It is a type (if we interpret it as such), that is enough.


But, all of this said, this does bring me to a different concern.

If we consider \N\C::fooProto() as a type to be used within @var/@type/@param/@return, then is it really clear enough that this means "any callback that has the same signature as \N\C::fooProto()", or "any callback that has a @implements \N\C::fooProto() docblock"?

Or is this too much of a leap? Would the more natural/intuitive understanding be "the callback that references \N\C::fooProto() itself"? E.g. the string "N\\C::fooProto", or the array ["N\\C", "fooProto"]?

@mvriel
Copy link
Member

mvriel commented Dec 27, 2015

Imo, we should forget about this "everything is an fqsen". In @param, we could say "everything is a type", and then we simply need to say that a callback signature specifier IS a type. Yes, it should be specified with the fully qualified namespace, but I don't care if the complete thing including the brackets is considered an fqsen. It is a type (if we interpret it as such), that is enough.

I am not a big fan of making a FQSEN count as a Type; it seems to me that we're trying to shoe-horn a desired feature in the wrong place. As I see it, what you want is to provide meta-data about the Type (which is callable).

Have you checked how other languages solve this issue? I can imagine that JSDoc or RDoc may have hit this bump before?

If other languages do not have a similar construct then I propose we use the Inline PHPDoc style together with a new tag to indicate what the expected signature for a callback would be.

For example something like this:

/**
 * @param callable $f {
 *     @signature (int $a, int $b)
 * }
 */

Though I must address that we are now discussing two things at the same time:

  1. being able to hint that a function matches the signature of an invoking entity
  2. being able to indicate the signature of a passed callable

I suggest focussing on the former for now and starting a new discussion for the second since they seem to be only remotely related to eachother

@donquixote
Copy link
Author

Though I must address that we are now discussing two things at the same time:

  1. being able to hint that a function matches the signature of an invoking entity
  2. being able to indicate the signature of a passed callable

I think these two things depend on each other, and that at least for a while it is better to discuss them in the same issue. But if you disagree, we could open a new one for the @type/@param stuff.

I am not a big fan of making a FQSEN count as a Type

Well.. what I suggest is not that every FQSEN is a Type, but that \N\C::foo() is a type, a specialization of callable. I am not sure that \N\C::foo() is even an FQSEN, and I say it does not matter.
We could change the syntax of this, if people don't like it.

As I see it, what you want is to provide meta-data about the Type (which is callable).

Well.. \N\C[] is a specialization of array, and yet we do not write array<\N\C>, but rather \N\C[], which does not contain the term array. We also don't do object<\N\C>, even though it is a specialization of object.
I am not saying it is wrong to do sth like callable<...>, I just say I do not consider it necessary.

If other languages do not have a similar construct

https://en.wikipedia.org/wiki/Type_signature This is quite interesting.
But I don't see anywhere the idea to use a "prototype function" for documentation. Maybe this is new. Or maybe we need to look more.

@Fleshgrinder
Copy link

Or where do you see the problem?

All concerns I raised are more of a rhetorical nature and not true concerns of mine. All variations work, the question is, which variation is the one that is best for tools and IDEs.

Well.. I prefer the brackets, makes it more obvious to everyone on first sight.

I also prefer it with the brackets to make it more clear. PhpStorm does not care; how does phpDocumentor handle that?

On the other hand, a procedural function and a class with the same name can coexist. So yeah, this would be a reason to require the brackets.

It also collides with namespace and class constants because casing does not matter. However, I do not really see an issue here because tools would need to look the thingy up anyways and display an error if it cannot be found, hence, if it is found and is e.g. a constant or a class or whatever thingy that makes no sense an error needs to be issued as well. No problem there imho.

Have you checked how other languages solve this issue? I can imagine that JSDoc or RDoc may have hit this bump before?

Well, JSDoc has the @callback tag and uses it within the @param tag. We do not really need the @callback tag because all structural elements are automatically addressable via their FQSEN and that is why I would extend the @param type to support FQSENs. I do not think that this is tucking in of a feature, on the contrary. It makes imho absolute sense to allows them because we already allow FQCNs and we simply extend them. This would also allow other interesting things like giving a range of constants that are allowed. However, as always I would like to see support for short names as PhpStorm supports it.

Well.. what I suggest is not that every FQSEN is a Type, but that \N\C::foo() is a type, a specialization of callable. I am not sure that \N\C::foo() is even an FQSEN, and I say it does not matter.
We could change the syntax of this, if people don't like it.

It is an FQSEN, specifically for a method but we would need to support functions as well (PCRE but not necessarily correct):

method = /^(\\[a-z_][a-z0-9_]*)+::[a-z_][a-z0-9_]*\(\)$/i
function = /^(\\[a-z_][a-z0-9_]*)*\\?[a-z_][a-z0-9_]*\(\)$/i

As I see it, what you want is to provide meta-data about the Type (which is callable).

Well.. \N\C[] is a specialization of array, and yet we do not write array<\N\C>, but rather \N\C[], which does not contain the term array. We also don't do object<\N\C>, even though it is a specialization of object.
I am not saying it is wrong to do sth like callable<...>, I just say I do not consider it necessary.

Writing \N\C[] is just a variation of array<\N\C> and writing \N\C is just a variation of object<\N\C>. Both work because there is consensus that their meaning is equivalent. We can introduce the same for callable: Anything that ends with and opening and closing bracket is a callable, e.g. fn(), \fn(), N\C::fn(), \N\C::fn(), ... I actually really like that and it would work nicely together with type hints.

The following example is valid PHP code:

<?php

namespace N {
    class C {
        public function cb() {
            return 'called';
        }
    }
}

namespace {
    /** @var \N\C::cb() $cb */
    $cb = [ new N\C(), 'cb' ];

    /** @param \N\C::cb() $cb */
    function fn(callable $cb) {
        echo $cb();
    }

    fn($cb);
}

@Fleshgrinder
Copy link

/**
 * @param callable $f {
 *     @signature (int $a, int $b)
 * }
 */

That would be the other way, when you want to indicate a caller how the callable would be called. I think that is even more useful than the feature described in my last comment. However, I think that the syntax your propose is overly complicated. Why not simply use PHP 7 syntax?

/**
 * @param {callable(int $a, int $b): string} $cb
 */

Note how I use the braces that we are currently discussing to allow specification of the return type as well. The callable cannot clash with any user defined thingy because it is a reserved name and the signature can be parsed with a normal PHP signature parser. Of course, more complex types cannot be expressed, namely any complex type that cannot be expressed with PHP 7. But I think that it is sufficient to keep that in the description than.

@donquixote
Copy link
Author

/**
 * @param callable $f {
 *     @signature (int $a, int $b)
 * }
 */

I have an interesting idea extending this:

/**
 * @param callable $f {
 *     @param int $a
 *     @param string $b
 *     @return X
 *     @throws FooException
 * }
 * @return X|null
 */
function foo(callable $f) {
  try {
    return $f(5, 'x');
  }
  catch (FooException $e) {
    return null;
  }
}

This would give us the full expressiveness of phpDocumentor, and allow even explanation text.

Otherwise, if we stick to native PHP signature syntax, I find the syntax suggested in Fleshgrinder's last post shorter and preferable.

@donquixote
Copy link
Author

In the big picture, I now see the following partial proposals, which can all coexist, mostly, and each have their own specific use case:

  1. Refine a @param callable $f, @type callable or @var callable with..
    1. ..an explicit signature specification, native PHP 7 style.
    2. ..an explicit signature specification, phpDocumentor style.
    3. ..a reference to a prototype function, to specify the callable signature.
    4. ..a reference to a prototype function, to specify the callable signature AND semantics.
  2. Enhance a function / method declaration, with..
    1. ..a reference to a prototype function, to specify the callable signature.
    2. ..a reference to a prototype function, to specify the callable signature AND semantics.

If we can agree on a plan similar to this, we can then discuss the details in separate issues.

@henrywright
Copy link

May I suggest you use something like @type literal to distinguish between named and anonymous functions? Just an idea. For example:

/**
 * This is a summary
 *
 * @param string $var A pub in London.
 * @return callable
 */
function named($var) {
    /**
     * This is a summary
     *
     * @type literal
     *
     * @param string $val A restaurant in London.
     * @uses string $var A pub in London.
     */
    return function($val) use($var) {
        echo "I went to $val and then $var";
    };
}

@donquixote
Copy link
Author

@henrywright

May I suggest you use something like @type literal to distinguish between named and anonymous functions?

I don't quite get it..

@donquixote
Copy link
Author

Let's continue part of the discussion over at #1712.

This leaves for this discussion:

  1. Enhance the "callable" type specifier for @param, @return, @type and @var with..
    1. ..a reference to a prototype function, to specify the callable signature.
    2. ..a reference to a prototype function, to specify the callable signature AND semantics.
  2. Enhance the documentation of a function / method declaration, with..
    1. ..a reference to a prototype function, to declare that the signature of the function/method defined here should match that of the prototype.
    2. ..a reference to a prototype function, to declare that the signature AND semantics of the function/method defined here should match the prototype.

@henrywright
Copy link

@donquixote

@type literal would indicate the element being documented is a function literal (an anonymous, unnamed function), and not a function bound to an identifier such as a defined function:

function my_func() {
    // Inside the my_func() definition.
}

@donquixote
Copy link
Author

@henrywright
In the example you provide, the @type literal is redundant, because it can be seen from the code below that this is a closure.
It would provide more information as part of a @return, @param, @var or @type, to document that the value being passed around is indeed a closure, not just any callable. But for these purposes, there is not really any place for an additional @type tag.
And, isn't this equivalent with simply using "Closure" as the type specifier? Since for any lambda, $f instanceof Closure is true.

Finally, what is the benefit of knowing something is an anonymous function, and not another type of callable?
Or, what is a use case for a variable or parameter that we expect or require to always be a closure, and never another type of callable?

@henrywright
Copy link

I see your point @donquixote. Sorry to have derailed the conversion.

@sun
Copy link

sun commented Jan 20, 2016

The @implements tag could make sense in situations where we actually want to refer to an interface; e.g. traits that implement interfaces.

The trait @implements the methods of the interface then, correct.

A class could additionally denote via @implements that its methods are strictly adhering to the interface, correct.

A disconnected function X that @implements the signature (and possibly even the return type) of function Y, is correct, too.

In fact, it's all one and the same. (Especially because @implements was invented long before Traits, so if it's valid for traits...)

No need to limit this to certain language constructs. Every case has the identical meaning: fulfilling the specified contract.

Various other PSR-5/PHPDoc constructs accept a mixture of FQSENs, referencing and triggering different meanings. Exactly the same happens here.

Arguing for this proposal doesn't mean that I'm a fan of the architectural pattern, nor would I advise anyone to base their application's architecture around such loose callbacks today. Even the OP made these points crystal clear. The subjective interpretation of @implements to "only make sense for object-oriented code" (here and on the mailing list) is inadequate and simply not true. Get over it, please.

@donquixote
Copy link
Author

@sun I mostly agree, just a few comments.

The @implements tag could make sense in situations where we actually want to refer to an interface; e.g. traits that implement interfaces.

For traits, in some cases, it could actually make sense to document specific methods with @implements, instead of the entire trait:

  • If the methods are going to be renamed in the use statement. So in the trait, the method has a different name than in the interface.
  • If the trait implements methods from multiple, unrelated interfaces.

A class could additionally denote via @implements that its methods are strictly adhering to the interface, correct.

Generally this would be just a duplication of the native implements declaration, right? So it would be very very rare that this is needed or desirable.

Every case has the identical meaning: fulfilling the specified contract.

Yes. Of course, a method/function can not implement an interface, and a class cannot implement a method/function.
And, a method/function (unless abstract) can be the implementation and the contract at the same time.

Arguing for this proposal doesn't mean that I'm a fan of the architectural pattern, nor would I advise anyone to base their application's architecture around such loose callbacks today.

What we do in Drupal and Wordpress may be a bit rotten indeed. But the concept of "functional programming" is still valid in modern times. Especially with the addition of closures/lambdas.

@donquixote donquixote changed the title Introduce procedural @implements tag.. Introduce procedural / functional @implements tag.. May 17, 2016
@donquixote
Copy link
Author

For some time I have been wondering if we lose semantic information by switching from a one-method OOP interface to a callback signature which only specifies parameter types and return type. Here someone had the same question:
http://programmers.stackexchange.com/questions/286942/is-the-semantic-contract-of-an-interface-oop-more-informative-than-a-function?rq=1

In previous comments I said that we should distinguish @implements for semantic contracts, vs @signature for non-semantic contracts.

The accepted stackexchange answer proposes to use value objects / value types to make a signature more specific and more semantic.

I think we still want both in PHP, because we cannot expect everyone to use value objects / value types. But it is still interesting read related to this discussion :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Needs triage
Issue Triage
  
Needs triage
Development

No branches or pull requests

6 participants