On Sun, Jan 3, 2021, at 8:28 AM, Olle Härstedt wrote:

> >> I like that you connect higher level design patterns with language
> >> design. This is the way to go, IMO. Personally, I'd prefer support for
> >> the Psalm notation `@psalm-readonly`, which is the same as your
> >> initonly. Clone-with makes sense too, as this construct is already
> >> supported in multiple languages. The exact notation doesn't matter
> >> that much - my personal choice is OCaml {record with x = 10} over JS
> >> spread operator, but OCaml is pretty "wordy" in notation in contrast
> >> to the C tradition that PHP is part of.
> >>
> >> Reintroducing "objects that pass by value" is a hard pass from me. The
> >> way forward is immutability and constrained mutability (ownership,
> >> escape analysis, etc). Psalm also supports array shapes - maybe this
> >> can be investigated as an alternative? Since PHP has no tuples.
> >>
> >> I'm not convinced the added complexity of asymmetric visibility is
> >> powerful enough to motivate its existence. Feel free to prove me
> >> wrong. :) My choice here would be namespace "internal" (also supported
> >> by Psalm already), but this requires implementation of namespace
> >> visibility, a PR that was abandoned.
> >>
> >> And also, happy new year!
> >
> > Happy New Year!
> >
> > I agree that "objects, but passing by value" would not be the right
> > solution. I used to think that would be a good part of the solution, but
> > eventually concluded that it would introduce more complexity, not less.
> > Eventually, everything people wanted to do with objects they'd want to do
> > with "Records" (for lack of a better term), and if they pass by value but
> > are still mutable then you have a weird situation where sometimes changes
> > propagate and some don't (depending on if you have a record or object).
> > Making it easier to use objects in a value-esque way will get us closer to
> > the desired end state.
> >
> > I think the tldr of my post is this: A single "immutable" flag (whatever
> > it's called) on a class or property would require having lots of holes poked
> > in it in order to make it useful in practice (mostly what "initonly" would
> > do), but those holes would introduce other holes we don't want (cloning an
> > object from the outside when you shouldn't).
> 
> I new language feature needs to be both simple and powerful - it's not
> enough to be only powerful. A second problem I see is how asymmetric
> visibility would affect the readability of a class, putting extra
> strain in understanding it. Thirdly, how does PHP differ from FP
> languages like OCaml and Haskell in this regard, neither who uses
> visibility in this way? What's acceptable in those languages that
> would be unacceptable in PHP?
> 
> Olle

I'll disagree slightly.  A language feature should introduce more power than it 
does complexity.  Not everything *can* be made absolutely simple, but the power 
it offers is worth it.  I'd say it should minimize introduced complexity, 
relative to the power offered.  Complexity ideally is super low, but it's never 
zero simply by virtue of being "one more thing" that developers need to know 
how to read.

So in this case, we need to compare the power/complexity of asymmetric 
visibility vs the power/complexity of "immutable... except in these 
situations."  I would argue that asymmetric visibility is more 
self-documenting, because it states explicitly what those situations are.

The other point is that, as noted, "initonly" creates a gap if you have 
properties that are inter-dependent.  Those then cannot be made public-read, 
because that would also mean public-clone-with, and thus allow callers to 
violate property relationships.  Asymmetric visibility does not have that 
problem.

As far as other language comparisons, I've never written in OCaml and can only 
barely read Haskell. :-)  However, the relevant points as I understand them are:

* In strictly functional languages (Haskell, etc.), immutability is assumed by 
default.  So the rest of the syntax, runtime behavior, and community standards 
are built on that assumption.  That's not true in PHP.

* Haskell at least (and I presume other strictly functional languages, although 
I've not dug into them in any detail at all) know you're going to be calling a 
bazillion functions, often recursively, and so the engine can reorder things, 
execute lazily, skip having a stack entirely, or do other things to make a 
deeply recursive function design highly performant.  That's not the case in 
PHP, so usually an iterative algorithm is going to be more performant but 
requires mutating variables.  So the engine is optimized for that by default.

Compare the idealized functional/immutable fibbonaci with its mutable-iterative 
version:

function fp_fib(int $n) {
  return match($n) {
    0, 1 => 1,
     default => fp_fib(n-1) - fp_fib(n-2),
  };
}

function fibonacci_iterative(int $n)
{
    $previous = 1;
    $current = 1;
    $next = 1;
    for ($i = 3; $i <= $n; ++i) {
        $next = $current + $previous;
        $previous = $current;
        $current = $next;
    }
    return $next;
}

That means you'll often have optimizations where you want to write mutable code 
in the small in order to create effectively-immutable at a higher level.  We do 
that now for with-er methods, which mutate the $new object by necessity before 
returning it, resulting in an object that seems immutable from the outside.

Rust is able to do some of the same kind of thing with zero cost abstraction 
because its variables are mostly-immutable.

* I've read very good arguments that class-level visibility control is a 
mistake and always was.  Visibility should be at the package level, not the 
object level, which is what Go, Rust, and many other newer languages do.  PHP 
doesn't have packages and it does have class-based visibility, for better or 
worse, and neither of those are changing any time soon.  Package-level 
visibility provides a different level at which to have the "immutable on the 
outside, but internally we can optimize things" barrier that is, arguably, 
better.  In PHP, we're stuck with class-level visibility so that's what we've 
got to work with.

That's all background on why I think, in PHP specifically, letting developers 
emulate immutability at the level they need rather than forcing it tightly at 
the language level is going to be a better strategy.

(But, of course, I could be convinced otherwise with sufficient demonstrated 
use cases, but they'd have to address the drawbacks of "immutable except for" I 
noted previously.)

It sounds like no one is against clone-with, though? :-)  Anyone want to argue 
for clone-arguments?

--Larry Garfield

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

Reply via email to