rfc:allow_casting_closures_into_single-method_interface_implementations

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:allow_casting_closures_into_single-method_interface_implementations [2023/04/25 10:22] nicolasgrekasrfc:allow_casting_closures_into_single-method_interface_implementations [2023/04/25 19:36] (current) nicolasgrekas
Line 1: Line 1:
-===== Title: Allow casting closures into single-method interface implementations =====+====== PHP RFC: Allow casting closures into single-method interface implementations ======
  
   * Version: 1.0   * Version: 1.0
Line 8: Line 8:
   * Implementation: TBD   * Implementation: TBD
  
-==== Introduction ====+===== Introduction =====
  
 This RFC proposes a new method for the <php>Closure</php> class called <php>castTo()</php>. This method would allow developers to create an instance of a class that implements a specified interface and uses the closure as the implementation. This RFC proposes a new method for the <php>Closure</php> class called <php>castTo()</php>. This method would allow developers to create an instance of a class that implements a specified interface and uses the closure as the implementation.
  
-==== Proposal ====+===== Proposal =====
  
 A new method, <php>castTo()</php>, would be added to the <php>Closure</php> class with the following signature (generics added for extra clarity): A new method, <php>castTo()</php>, would be added to the <php>Closure</php> class with the following signature (generics added for extra clarity):
Line 61: Line 61:
   * **Providing a type-safe alternative to the <php>Closure</php> type**: instead of typing against the <php>Closure</php> type, developers could declare and use interfaces that have a single <php>__invoke()</php> method. While this is possible already, this is quite rare in practice because it comes at a high syntactic cost for end users, which are then required to define a full class. By allowing to quickly turn a closure into an implementation of such interfaces, the <php>castTo()</php> method would greatly reduce this boilerplate, leading to a wider adoption of type-safe code for closures.   * **Providing a type-safe alternative to the <php>Closure</php> type**: instead of typing against the <php>Closure</php> type, developers could declare and use interfaces that have a single <php>__invoke()</php> method. While this is possible already, this is quite rare in practice because it comes at a high syntactic cost for end users, which are then required to define a full class. By allowing to quickly turn a closure into an implementation of such interfaces, the <php>castTo()</php> method would greatly reduce this boilerplate, leading to a wider adoption of type-safe code for closures.
  
-=== Example 1: Using a closure as a default implementation for a TranslatorInterface ===+==== Example 1: Using a closure as a default implementation for a TranslatorInterface ====
  
 This example demonstrates how to use the <php>castTo()</php> method to create a default implementation for a ''TranslatorInterface'' using a closure. It combines this proposal with the first-class callable syntax. This example demonstrates how to use the <php>castTo()</php> method to create a default implementation for a ''TranslatorInterface'' using a closure. It combines this proposal with the first-class callable syntax.
Line 85: Line 85:
 If no custom implementation of ''TranslatorInterface'' is provided during the instantiation of ''SomeClass'', a default implementation is  derived using a closure created from the <php>strtr()</php> function. If no custom implementation of ''TranslatorInterface'' is provided during the instantiation of ''SomeClass'', a default implementation is  derived using a closure created from the <php>strtr()</php> function.
  
-=== Example 2: Type-safe alternative to using Closure ===+==== Example 2: Type-safe alternative to using Closure ====
  
-Imagine that you have the following function, that takes a closure and two number to do some operation on them:+Imagine that you have the following function, that takes a closure and two numbers to do some operation on them:
  
 <code php> <code php>
-function executeOperation(Closure $operation, int $a, int $b): int { +function executeOperation(Closure $operator, int $a, int $b): int { 
-    return $operation($a, $b);+    return $operator($a, $b);
 } }
 </code> </code>
Line 102: Line 102:
 } }
  
-function executeOperation(InvokableInterface $operation, int $a, int $b): int { +function executeOperation(OperatorInterface $operator, int $a, int $b): int { 
-    return $operation($a, $b);+    return $operator($a, $b);
 } }
  
Line 126: Line 126:
 In this example, an ''OperatorInterface'' is defined with a single <php>__invoke()</php> method, which can be used to replace the <php>Closure</php> type on the <php>executeOperation()</php> function. The <php>castTo()</php> method is then used to create instances of the interface, with different behaviors implemented by closures. This approach provides a more type-safe and expressive way to handle closures while adhering to the required interface. In this example, an ''OperatorInterface'' is defined with a single <php>__invoke()</php> method, which can be used to replace the <php>Closure</php> type on the <php>executeOperation()</php> function. The <php>castTo()</php> method is then used to create instances of the interface, with different behaviors implemented by closures. This approach provides a more type-safe and expressive way to handle closures while adhering to the required interface.
  
-=== Example 3: Decoupling URI template implementations using adapters ===+==== Example 3: Decoupling URI template implementations using adapters ====
  
 This example demonstrates how the <php>castTo()</php> method can be used to create adapters for This example demonstrates how the <php>castTo()</php> method can be used to create adapters for
Line 168: Line 168:
  
 In this example, an ''UriExpanderInterface'' is defined with an <php>expand()</php> method. In this example, an ''UriExpanderInterface'' is defined with an <php>expand()</php> method.
- 
 3 different implementations of the interface are derived using the <php>castTo()</php> method. 3 different implementations of the interface are derived using the <php>castTo()</php> method.
- 
 Finally, the <php>getExpandedUrl()</php> function is defined, which accepts an implementation of Finally, the <php>getExpandedUrl()</php> function is defined, which accepts an implementation of
 <php>UriExpanderInterface</php> and expands a URI template using the provided implementation. <php>UriExpanderInterface</php> and expands a URI template using the provided implementation.
Line 210: Line 208:
             PHP);             PHP);
  
-        $cache[$key] = $object::class;+        $cache[$cacheKey] = $object::class;
  
         return $object;         return $object;
Line 220: Line 218:
 https://github.com/tchwork/closure-caster/blob/main/src/function.php https://github.com/tchwork/closure-caster/blob/main/src/function.php
  
-==== Backward Incompatible Changes ====+===== Backward Incompatible Changes =====
  
 This proposal introduces no backward incompatible changes, as it only adds a new method to the <php>Closure</php> class. This proposal introduces no backward incompatible changes, as it only adds a new method to the <php>Closure</php> class.
  
-==== Open Issues ====+===== Open Issues =====
  
   * exact behavior when checks fail   * exact behavior when checks fail
Line 230: Line 228:
   * would the engine allow implementing this without adding any extra frame in the call stack?   * would the engine allow implementing this without adding any extra frame in the call stack?
  
-==== Unaffected PHP Functionality ====+===== Unaffected PHP Functionality =====
  
 No existing PHP functionality would be affected by this proposal. No existing PHP functionality would be affected by this proposal.
  
-==== Future Scope ====+===== Future Scope =====
  
   * Allow a closure to declare a single-method interface it implements, as in e.g. <php>function () implements FooInterface {...}</php>. See https://wiki.php.net/rfc/allow-closures-to-declare-interfaces-they-implement   * Allow a closure to declare a single-method interface it implements, as in e.g. <php>function () implements FooInterface {...}</php>. See https://wiki.php.net/rfc/allow-closures-to-declare-interfaces-they-implement
Line 241: Line 239:
 Both ideas are complementary to the one proposed in this RFC. Both ideas are complementary to the one proposed in this RFC.
  
-==== Proposed PHP Version ====+===== Proposed PHP Version =====
  
 This RFC targets PHP version 8.3. This RFC targets PHP version 8.3.
  
-==== Vote ====+===== Vote =====
  
 The vote will require a 2/3 majority to be accepted. Voting will start on [Vote start date] and end on [Vote end date]. The vote will require a 2/3 majority to be accepted. Voting will start on [Vote start date] and end on [Vote end date].
  
  
rfc/allow_casting_closures_into_single-method_interface_implementations.1682418141.txt.gz · Last modified: 2023/04/25 10:22 by nicolasgrekas