2020-09-13 17:58 GMT, Benjamin Eberlei <kont...@beberlei.de>:
> On Sat, Sep 12, 2020 at 10:23 PM Olle Härstedt <olleharst...@gmail.com>
> wrote:
>
>> Hi internals!
>>
>> Separation of data and behaviour is both a fun and hard discussion,
>> especially considering:
>>
>> * "It should be possible to add new features without touching old code";
>> and
>> * "Principle of Least Privilege" (never expose more than you have to)
>> (https://en.wikipedia.org/wiki/Principle_of_least_privilege).
>>
>> There should (could) be a way to add new behaviour to old data without
>> touching the old data (class). Traits won't work in this use-case,
>> since they assume the same internal structure for all trait-using
>> classes. Imagine the `stringable` interface and a `toString` trait. A
>> __toString() method needs knowledge about the internal structure of a
>> class Foo. Yet if we want to keep adding behaviour to Foo, we'll end
>> up with either exposing too much of Foo, or expanding the class file
>> indefinitely. Please note that composition is not a proper solution,
>> since it requires exposure of Foo; composition leads to lack of proper
>> encapsulation, or representation exposure.
>>
>> In Haskell it's possible to split instance implementation of
>> type-classes into separate files. In Rust you can have a struct with
>> private fields and put impl of behaviour in different files (but same
>> crate).
>>
>> A similar feature in PHP could look like (using new keyword `expand`
>> but could be anything, or even `extend` in new context):
>>
>> ```
>> // File FooStringable.php
>> expand Foo implements stringable {
>>   public function __toString() {
>>     // Full access to Foo's all private fields here.
>>     // Assumes you can autoload Foo.
>>     // Assumes usage of $foo->__toString(); will be configured with
>> autoload to dynamically find the correct behaviour of Foo.
>>   }
>> }
>> ```
>>
>> If you'd use composition instead, you'd maybe have a formatter class
>> with a method `$formatter->toString(stringable $foo)`. This has the
>> problem I mentioned with exposing too much of $foo; it breaks
>> encapsulation. It has the benefit of being able to provide multiple
>> toString methods with different formats, but would have to assume
>> similar structure of the objects passed to it (defined with an
>> interface), which is not always possible or desirable.
>>
>> The other way is inheritance, which doesn't scale over multiple
>> behaviours. `FooWithStringable extends Foo`? No.
>>
>> Was I clear here? Do you understand the issues that this design
>> pattern is trying to solve? Its purpose is to solve "keep adding new
>> feature to old data" in a clean and proper way, while keeping
>> information encapsulation.
>>
>
> Do I understand you correctly, it would be somewhat like "opening" up a
> class and making changes to it in another file?
>
> Certainly a powerful concept, but I would be very interested in the details
> how that would interact with autoloading. If I have a class Foo loaded, and
> its "extension" FooString with toString method not, then it would lead to
> the "toString" code missing.

Yes, a little like opening up, *but* with clear restrictions. It was
explained to me that this won't work without either:

1) A module system to define which files are part of a class
2) Manually write in the "main" class file which other extensions to
this class should be loaded.

The reason is again encapsulation - it should not be possible for any
file to just get access to private fields by adding a new interface
implementation.

Option (2) can be achieved if we allow "include <file>;" in PHP
*inside* a class definition. Again note that this is different from a
trait, since it gives access to private properties that are
*different* between the classes using it, like toString() or
toQuery().

Option (2) does not need configured autoloading. Option (1) is more
elaborate, maybe composer.json would need to configure something, *or*
it is assumed that a class is defined in a single folder instead of a
single file. Which already is kind of like a module system.

In Rust, it's possible to spread out behaviour (impl) to a certain
data (struct) in the same _crate_ while keeping access to private
fields: 
https://users.rust-lang.org/t/implement-private-struct-in-different-files/29407

C# has something called "partial class":
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods

Olle

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to