rfc:immutability

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:immutability [2016/09/10 20:38] – Added immutable modifier for interfaces. marijic.silviorfc:immutability [2018/02/20 11:19] (current) marijic.silvio
Line 1: Line 1:
 ====== PHP RFC: Immutable classes and properties ====== ====== PHP RFC: Immutable classes and properties ======
-  * Version: 0.1 +  * Version: 0.2 
-  * Date: 2016-08-12+  * Date: 2018-02-19
   * Author: Michal Brzuchalski <michal@brzuchalski.com>   * Author: Michal Brzuchalski <michal@brzuchalski.com>
   * Author: Silvio Marijic <marijic.silvio@gmail.com>   * Author: Silvio Marijic <marijic.silvio@gmail.com>
-  * Status: In Draft+  * Status: In Discussion
   * First Published at: [[rfc:immutability|https://wiki.php.net/rfc/immutability]]   * First Published at: [[rfc:immutability|https://wiki.php.net/rfc/immutability]]
  
 ===== Introduction ===== ===== Introduction =====
  
-This RFC proposes introduction of immutable classes and properties. Currently PHP lacks native support for immutability. Because of that user-land applications are using third party libraries or resort to custom implementations and still there is no easy enforcement of immutability. Introducing this feature would help bring one unified solution to this problem, and also it would remove unnecessary logic from user-land applications.+This RFC proposes the introduction of immutable classes and properties. Currently, the only way of achieving immutability is through encapsulation. Because of thatuser-land applications are using third party libraries or resort to custom implementations, but there is still no easy enforcement of immutability. Introducing this feature would help bring one unified solution to this problem, and also it would remove unnecessary logic from user-land applications
 +If this mechanism is introduced, developers can be sure that they are programming with no side effects, meaning that state of the object can not be changed without developers being made aware. This is especially useful when dealing with concurrency, where we can guarantee that threads will always read the same value.
  
  
 **Pros** **Pros**
   - Immutability guaranteed by language instead of user-land implementation.   - Immutability guaranteed by language instead of user-land implementation.
-  - Memory optimizations.+  - Programming with no side effects.
   - Safe for concurrency.    - Safe for concurrency. 
   - Value objects, DTO's etc. can be easily created.   - Value objects, DTO's etc. can be easily created.
-  - Properties can be public which removes need for getters without allowing state modification.+  - Properties can be public which removes the need for getters without allowing state modification.
   - (Please point it out more advantages)   - (Please point it out more advantages)
  
 **Cons** **Cons**
-  - (Please point it out more disadvantages)+  - Currently arrays on immutable properties are not supported.
  
  
-**Before** +===== Proposal =====
-<code php> +
-<?php+
  
-class Email { +==== Immutable Class ====
-  private $_email;+
  
-  public function __construct ($email) { +A class defined as immutable will imply immutability across all of its properties by default. After the object is constructed, it is not possible to modify the state of the object from any scope.
-    // validation +
- +
-    $this->_email = $email; +
-  } +
- +
-  public function getValue() { +
-     return $this->_email; +
-  } +
-+
-</code>+
  
-**After** 
 <code php> <code php>
-<?php 
  
 immutable class Email { immutable class Email {
Line 51: Line 37:
  
   public function __construct ($email) {   public function __construct ($email) {
-    // validation 
- 
     $this->email = $email;     $this->email = $email;
   }   }
 } }
 +
 +$email = new Email("foo@php.net");
 +$email->email = "bar@php.net" // Call will result in Fatal Error
 +
 </code> </code>
  
-===== Proposal ===== +Changes to inheritance are made to add constraints when extending an immutable class - the child class must also be immutable in order to preserve immutability across the whole object.
- +
-Class defined as immutable will imply immutability across all of it's properties by default. After constructor returns, it will not be possible to modify state of immutable properties from any scope.+
  
 <code php> <code php>
 +immutable class Foo{}
 +class Bar extends Foo{} // Will result in Fatal Error
 +</code>
  
-immutable class Email { 
-  public $email; 
  
-  public function __construct ($email) { +==== Immutable Properties ====
-    // validation+
  
 +Classes have the ability to enforce immutability to only a subset of properties if needed, in that case, immutability will be implied only on properties that are declared as immutable.
 +
 +<code php>
 +class User {
 +  private $id;
 +  public immutable $email;
 +
 +  public function __construct ($id, $email) {
 +    $this->id = $id;
     $this->email = $email;     $this->email = $email;
   }   }
 } }
- 
-$email = new Email("foo@php.net"); 
-$email->email = "bar@php.net" // Call will result in Fatal Error 
- 
 </code> </code>
  
-Regular classes can define immutability per property. 
  
 +If an immutable property contains an object, to preserve immutability, the object that is being assigned to the immutable property must also be immutable.
 <code php> <code php>
-class Email {+immutable class Email{} 
 + 
 +class User {
   public immutable $email;   public immutable $email;
  
-  public function __construct ($email) { +  public function __construct (Email $email) {
-    // validation +
     $this->email = $email;     $this->email = $email;
   }   }
 } }
- 
-$email = new Email("foo@php.net"); 
-$email->email = "bar@php.net" // Call will result in Fatal Error 
 </code> </code>
  
-Any class that extends immutable class must be also declared as immutable.+Resources are not allowed to be assigned to immutable properties because of fact that resources by nature are not immutable
 +<code php> 
 +class File { 
 +  public immutable $handle;
  
 +  public function __construct ($handle) {
 +    $this->handle = $handle;
 +  }
 +}
 +
 +$file = new File(fopen('file.txt'));
 +</code>
 +
 +Arrays are not allowed to be assigned to immutable properties..
 <code php> <code php>
-immutable class Foo{} +class 
-class Bar extends Foo{} // Will result in Fatal Error+  public immutable $x; 
 + 
 +  public function __construct ($x) { 
 +    $this->x = $x; 
 +  } 
 +
 + 
 +$a = new A(['foo']);
 </code> </code>
  
 +If an immutable property of a parent class is overridden in a child class, it has to be declared as immutable.
 +Since the focus of this RFC is immutable objects, having static properties in immutable classes will result in a compile error.
 +
 +
 +==== References ====
 +
 +Assigning by references to immutable properties will result in an error, otherwise the object loses control over properties, and immutability can be broken.
  
-It will not be possible to assign value by reference to immutable property. 
 <code php> <code php>
 immutable class Email { immutable class Email {
Line 121: Line 134:
 </code> </code>
  
-It will not be possible to assign value by reference to immutable property.+==== Comparison ==== 
 + 
 +Identity of immutable object is based on its value. So two immutable are identical if they are of the same type and contain same value. 
 <code php> <code php>
 immutable class Email { immutable class Email {
Line 132: Line 148:
   }   }
 } }
-</code> 
  
-Interfaces can be declared as immutable which results requirement that any class implementing immutable interface must be declared as immutable.. +$email1 = new Email("foo@php.net")
- +$email2 = new Email("foo@php.net"); 
-<code php> +var_dump($email1 === $email2); // bool(true)
- +
-immutable interface ValueObject {} +
- +
-class Foo  implements ValueObject{ +
-  public $foo; +
- +
-  public function __construct ($foo) { +
-    // validation +
- +
-    $this->foo = $foo; +
-  } +
-+
- +
-// Will result in a error because Foo is not immutable class but implements immutable interface.+
 </code> </code>
- 
  
 ===== Examples ===== ===== Examples =====
-Notice in above examples removing getters and setting properties to public is optional. They simply doesn't need to be protected anymore in fact that immutable class objects are deeply frozen with eceptions on write.+Notice in above example, changing getters and setters methods to public properties is optional. They simply don't need to be protected anymorein factimmutable class objects are deeply frozen with exceptions on write.
  
-Every example shows where internal object state is important. Any references to objects passed into immutable class constructor cannot be references to scalars or may be immutable class instances. That gives high guarantee you will keep internal state unchanged for all the time.+Every example shows where internal object state is important. Any references to objects passed into an immutable class constructor cannot be references to scalars or may be immutable class instances. That gives high guarantee you will keep internal state unchanged for all the time.
  
 ---- ----
Line 164: Line 164:
 ==== Money ==== ==== Money ====
  
-Money Pattern, defined by Martin Fowler and published in Patterns of Enterprise Application Architecture, is a great way to represent value-unit pairs. It is called Money Pattern because it emerged in a financial context.+Money Pattern, defined by Martin Fowler and published in Patterns of Enterprise Application Architecture, is a great way to represent value-unit pairs. It is called the Money Pattern because it emerged in a financial context.
  
 <code php> <code php>
Line 298: Line 298:
 </code> </code>
  
-There is no need for getters because internally immutable object if deeply frozen and none of his properties cannot be written anymore. All properties accepts scalar values or objects which implements immutable classes so there is high guarantee such Money object will keep his internal state untouched.+There is no need for getters because this internally immutable object is deeply frozenand none of his properties cannot be written to anymore. All properties accept scalar values or objects which implement the immutable class, so there is high guarantee such Money object will keep its internal state untouched.
  
-==== URI ==== 
-Uri's domain is like Money complex ValueObjects and can be presented for eg. in this way. 
- 
-<code php> 
-class Scheme { 
- 
-    private $protocol; 
- 
-    private function __construct(string $protocol) { 
-        $this->protocol = $protocol; 
-    } 
- 
-    public function getProtocol() 
-    { 
-        return $this->protocol; 
-    } 
- 
-    public static function HTTP() { 
-        return new Scheme('http'); 
-    } 
-} 
- 
-class UserInfo { 
- 
-    private $username; 
-    private $password; 
- 
-    public function __construct(string $username, string $password) 
-    { 
-        $this->username = $username; 
-        $this->password = $password; 
-    } 
- 
-    public function getUsername() : string 
-    { 
-        return $this->username; 
-    } 
- 
-    public function getPassword() : string 
-    { 
-        return $this->password; 
-    } 
-} 
- 
-class Authority { 
- 
-    private $host; 
-    private $port; 
-    private $userInfo; 
- 
-    public function __construct(string $host, int $port = 80, UserInfo $userInfo = null) 
-    { 
-        $this->host = $host; 
-        $this->port = $port; 
-        $this->userInfo = $userInfo; 
-    } 
- 
-    public function getHost() : string 
-    { 
-        return $this->host; 
-    } 
- 
-    public function getPort() : int 
-    { 
-        return $this->port; 
-    } 
- 
-    public function getUserInfo() : UserInfo 
-    { 
-        return $this->userInfo; 
-    } 
-} 
- 
-class Path { 
- 
-    private $path; 
- 
-    public function __construct(string $path) 
-    { 
-        $this->path = $path; 
-    } 
- 
-    public function getPath() : string 
-    { 
-        return $this->path; 
-    } 
-} 
- 
-class Uri { 
- 
-    private $scheme; 
-    private $authority; 
-    private $path; 
- 
-    public function __construct(Scheme $scheme, Authority $authority, Path $path) 
-    { 
-        $this->scheme = $scheme; 
-        $this->authority = $authority; 
-        $this->path = $path; 
-    } 
- 
-    public function getScheme() : Scheme 
-    { 
-        return $this->scheme; 
-    } 
- 
-    public function getAuthority() : Authority 
-    { 
-        return $this->authority; 
-    } 
- 
-    public function getPath() : Path 
-    { 
-        return $this->path; 
-    } 
-} 
- 
-$phpNetUri = new Uri(Scheme::HTTP(), new Authority('php.net'), new Path('/downloads')); 
-</code> 
- 
-After refactoring to immutable class implementation the model is getting simplier: 
- 
-<code php> 
-immutable class Scheme { 
- 
-    /** @var string */ 
-    public $protocol; 
- 
-    private function __construct(string $protocol) { 
-        $this->protocol = $protocol; 
-    } 
-     
-    public static function HTTP() { 
-        return new Scheme('http'); 
-    } 
- 
-    public static function HTTPS() { 
-        return new Scheme('https'); 
-    } 
-} 
- 
-immutable class UserInfo { 
- 
-    /** @var string */ 
-    public $username; 
-    /** @var string */ 
-    public $password; 
- 
-    public function __construct(string $username, string $password) 
-    { 
-        $this->username = $username; 
-        $this->password = $password; 
-    } 
-} 
- 
-immutable class Authority { 
- 
-    /** @var string */ 
-    public $host; 
-    /** @var int */ 
-    public $port; 
-    /** @var UserInfo */ 
-    public $userInfo; 
- 
-    public function __construct(string $host, int $port = 80, UserInfo $userInfo = null) 
-    { 
-        $this->host = $host; 
-        $this->port = $port; 
-        $this->userInfo = $userInfo; 
-    } 
-} 
- 
-immutable class Path { 
- 
-    /** @var string */ 
-    public $path; 
- 
-    public function __construct(string $path) 
-    { 
-        $this->path = $path; 
-    } 
-} 
- 
-immutable class Uri { 
- 
-    /** @var Scheme */ 
-    public $scheme; 
-    /** @var Authority */ 
-    public $authority; 
-    /** @var Path */ 
-    public $path; 
- 
-    public function __construct(Scheme $scheme, Authority $authority, Path $path) 
-    { 
-        $this->scheme = $scheme; 
-        $this->authority = $authority; 
-        $this->path = $path; 
-    } 
-     
-    public function changeScheme(Scheme $scheme) : Uri { 
-        return new self($scheme, $this->authority, $this->path); 
-    } 
-} 
- 
-$http = new Uri(Scheme::HTTP(), new Authority('php.net'), new Path('/downloads')); 
-$https = $http->changeScheme(Scheme::HTTPS()); 
-</code> 
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-No backward incompatible changes.+No backwardly incompatible changes.
  
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
  
- - PHP 7.2+ To be discussed.
  
 ===== RFC Impact ===== ===== RFC Impact =====
Line 524: Line 317:
  
 ==== To Existing Extensions ==== ==== To Existing Extensions ====
- - Reflection.+ - Reflection is patched.
  
 ==== To Opcache ==== ==== To Opcache ====
Line 546: Line 339:
 ===== Future Scope ===== ===== Future Scope =====
  
 +Add support for arrays on immutable properties.
 +Expand immutability to regular variables also.
  
 ===== Proposed Voting Choices ===== ===== Proposed Voting Choices =====
Line 553: Line 348:
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
-[[https://github.com/php/php-src/compare/master...brzuchal:immutable]] +[[https://github.com/php/php-src/compare/master...smarijic:immutable-rfc]]
  
  
Line 564: Line 358:
  
 ===== References ===== ===== References =====
- 
  
  
 ===== Rejected Features ===== ===== Rejected Features =====
 + - Immutable interfaces
rfc/immutability.1473539892.txt.gz · Last modified: 2017/09/22 13:28 (external edit)