rfc:immutability
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:immutability [2016/08/14 20:23] – added code samples marijic.silvio | rfc:immutability [2016/12/02 14:54] – marijic.silvio | ||
---|---|---|---|
Line 2: | Line 2: | ||
* Version: 0.1 | * Version: 0.1 | ||
* Date: 2016-08-12 | * Date: 2016-08-12 | ||
- | * Authors: Michal Brzuchalski < | + | * Author: Michal Brzuchalski < |
+ | * Author: | ||
* Status: In Draft | * Status: In Draft | ||
* First Published at: [[rfc: | * First Published at: [[rfc: | ||
Line 8: | Line 9: | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | This RFC proposes introduction of immutable classes and properties. Currently | + | This RFC proposes introduction of immutable classes and properties. Currently |
+ | If those mechanizsm is utilized, developer can be sure that he is programming with no side effects, meaning that state of the object can not be changed with developer beeing unaware of that. This especialy comes hand in hand with concurrency when 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. | ||
+ | - 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. | ||
Line 19: | Line 22: | ||
**Cons** | **Cons** | ||
- | - Cloning has to be disabled for immutable objects | ||
- (Please point it out more disadvantages) | - (Please point it out more disadvantages) | ||
- | **Before** | + | ===== Proposal ===== |
- | <code php> | + | |
- | <?php | + | |
- | class Email { | + | ==== Immutable Class ==== |
- | private $_email; | + | |
- | public function __construct ($email) { | + | Class defined as immutable will imply immutability across all of it's properties by default. After object is constructor, |
- | // validation | + | |
- | $this-> | ||
- | } | ||
- | |||
- | public function getValue() { | ||
- | | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | **After** | ||
<code php> | <code php> | ||
- | <?php | ||
immutable class Email { | immutable class Email { | ||
Line 50: | Line 37: | ||
public function __construct ($email) { | public function __construct ($email) { | ||
- | // validation | ||
- | |||
$this-> | $this-> | ||
} | } | ||
} | } | ||
- | </ | ||
+ | $email = new Email(" | ||
+ | $email-> | ||
+ | </ | ||
+ | Changes to inheritance are made to add constraints when extending a immutable class, child class must also be immutable in order to to preserve immutability across whole object. | ||
+ | <code php> | ||
+ | immutable class Foo{} | ||
+ | class Bar extends Foo{} // Will result in Fatal Error | ||
+ | </ | ||
- | ===== Proposal ===== | ||
- | Class defined as immutable will imply immutability | + | ==== Immutable Properties ==== |
+ | |||
+ | Classes have ability to enforce | ||
<code php> | <code php> | ||
+ | class User { | ||
+ | private $id; | ||
+ | public immutable $email; | ||
- | immutable class Email { | + | |
- | public $email; | + | |
- | + | ||
- | | + | |
- | | + | |
$this-> | $this-> | ||
} | } | ||
} | } | ||
- | |||
- | $email = new Email(" | ||
- | $email-> | ||
- | |||
</ | </ | ||
- | Regular classes can define immutability per property. | ||
+ | If immutable property contains object, to preserve immutability, | ||
<code php> | <code php> | ||
- | class Email { | + | immutable |
+ | |||
+ | class User { | ||
public immutable $email; | public immutable $email; | ||
- | public function __construct ($email) { | + | public function __construct (Email $email) { |
- | // validation | + | |
$this-> | $this-> | ||
} | } | ||
} | } | ||
- | |||
- | $email = new Email(" | ||
- | $email-> | ||
</ | </ | ||
- | 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> | <code php> | ||
- | immutable | + | class File { |
- | class Bar extends Foo{} // Will result in Fatal Error | + | |
+ | |||
+ | public function __construct ($handle) | ||
+ | $this-> | ||
+ | | ||
+ | } | ||
+ | |||
+ | $file = new File(fopen(' | ||
</ | </ | ||
+ | If immutable property of parent class is overridden in child class, it has to be declared as immutable. | ||
+ | Since focus of this RFC is on immutable objects, having static properties in immutable classes will result in compile error. | ||
+ | |||
+ | |||
+ | ==== References ==== | ||
+ | |||
+ | Assigning by references to immutable properties will result with error, otherwise object looses controll 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 124: | Line 121: | ||
</ | </ | ||
- | If immutable | + | |
+ | ===== Examples ===== | ||
+ | Notice in above examples removing getters and setting properties to public is optional. They simply doesn' | ||
+ | |||
+ | Every example shows where internal | ||
+ | |||
+ | ---- | ||
+ | |||
+ | |||
+ | ==== Money ==== | ||
+ | |||
+ | Money Pattern, defined by Martin Fowler and published in Patterns of Enterprise Application Architecture, | ||
<code php> | <code php> | ||
+ | class Currency { | ||
- | class Bar{} | + | private $centFactor; |
+ | private $stringRepresentation; | ||
- | immutable class Foo { | + | private function __construct(int $centFactor, |
- | | + | $this-> |
+ | $this-> | ||
+ | } | ||
- | | + | |
- | | + | return $this-> |
+ | | ||
- | $this->bar = $bar; | + | |
- | } | + | return |
+ | } | ||
+ | |||
+ | public static function USD() : Currency { | ||
+ | return new self(100, ' | ||
+ | } | ||
+ | |||
+ | public static function EUR() : Currency { | ||
+ | return new self(100, ' | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Money { | ||
+ | |||
+ | private $amount; | ||
+ | private $currency; | ||
+ | |||
+ | public function __construct($amount, | ||
+ | $this-> | ||
+ | | ||
+ | } | ||
+ | |||
+ | public function getAmount() : float { | ||
+ | return $this-> | ||
+ | } | ||
+ | |||
+ | public function getCurrency() : Currency { | ||
+ | return $this-> | ||
+ | } | ||
+ | |||
+ | public function add(Money $other) : Money { | ||
+ | $this-> | ||
+ | return new Money($this-> | ||
+ | } | ||
+ | |||
+ | public function subtract(Money $other) { | ||
+ | $this-> | ||
+ | return new Money($this-> | ||
+ | } | ||
+ | |||
+ | public function multiplyBy($multiplier, | ||
+ | $product = round($this-> | ||
+ | return new Money($product, | ||
+ | } | ||
+ | |||
+ | private function ensureSameCurrencyWith(Money $other) { | ||
+ | if ($this-> | ||
+ | throw new \Exception(" | ||
+ | } | ||
+ | | ||
} | } | ||
- | $foo = new Foo(new Bar()); // Will result in a error because Bar is not instance of immutable class | + | $oneThousand |
</ | </ | ||
+ | |||
+ | After refactoring classes to immutable this example will look like this: | ||
+ | |||
+ | <code php> | ||
+ | immutable class Currency { | ||
+ | |||
+ | /** @var int */ | ||
+ | public $centFactor; | ||
+ | /** @var string */ | ||
+ | public $stringRepresentation; | ||
+ | |||
+ | private function __construct(int $centFactor, | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public static function USD() : Currency { | ||
+ | return new self(100, ' | ||
+ | } | ||
+ | |||
+ | public static function EUR() : Currency { | ||
+ | return new self(100, ' | ||
+ | } | ||
+ | } | ||
+ | |||
+ | immutable class Money { | ||
+ | |||
+ | /** @var float */ | ||
+ | public $amount; | ||
+ | /** @var Currency */ | ||
+ | public $currency; | ||
+ | |||
+ | public function __construct(float $amount, Currency $currency) { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function add(Money $other) : Money { | ||
+ | $this-> | ||
+ | return new Money($this-> | ||
+ | } | ||
+ | |||
+ | public function subtract(Money $other) { | ||
+ | $this-> | ||
+ | return new Money($this-> | ||
+ | } | ||
+ | |||
+ | public function multiplyBy($multiplier, | ||
+ | $product = round($this-> | ||
+ | return new Money($product, | ||
+ | } | ||
+ | |||
+ | private function ensureSameCurrencyWith(Money $other) { | ||
+ | if ($this-> | ||
+ | throw new \Exception(" | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $oneThousand = new Money(1000, Currency:: | ||
+ | </ | ||
+ | |||
+ | 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. | ||
Line 160: | Line 285: | ||
==== To Existing Extensions ==== | ==== To Existing Extensions ==== | ||
- | - Reflection. | + | - Reflection |
==== To Opcache ==== | ==== To Opcache ==== | ||
Line 189: | Line 314: | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | + | [[https:// | |
Line 200: | Line 324: | ||
===== References ===== | ===== References ===== | ||
- | |||
===== Rejected Features ===== | ===== Rejected Features ===== | ||
+ | - Immutable interfaces |
rfc/immutability.txt · Last modified: 2018/02/20 11:19 by marijic.silvio