Object properties, part 2: Examples

In my last post, I went over some of the pros and cons of various proposals for making PHP objects more immutable-ish, and the contexts in which they would be useful. I also posted the link to the PHP Internals list, where it generated some interesting if meandering discussion (as is par for the course on Internals).

One of the requests was for sample code to demonstrate why I felt particular feature proposals were better than others. Fair enough! This post is in response to that request, and I think it will help illuminate the challenges better.

For this exercise, I chose to experiment with a junior version of the PSR-7 request object as a concrete example. The code below is not exactly PSR-7; it's a representative sample of portions of a naive, slightly reduced scope version of PSR-7 requests only, and using all PHP 8.0 features available. The goal is not a complete working object, but sufficient real-world representative examples of situations that an immutability plan would need to address.

Continue reading this post on PeakD.

Larry 9 January 2021 - 6:33pm

Object properties and immutability

Submitted by Larry on 28 December 2020 - 5:30pm

There has been much discussion in recent weeks in PHP circles about how to make objects more immutable. There have been a number of proposals made either formally or informally that relate to object property access, all aimed at making objects safer through restricting write access in some way.

Since my last mega post on PHP object ergonomics was so well-received and successful (it resulted in both constructor promotion and named arguments being added to PHP 8.0, thanks Nikita!), I figure I'll offer another summary of the problem space in the hopes of a deeper analysis suggesting a unified way forward.

Continue reading on PeakD

Byte-sized functional programming: Composition over inheritance for functions, too

Submitted by Larry on 15 August 2020 - 10:07am

A popular refrain in object-oriented code is to favor object composition over inheritance. It offers more flexibility, less overhead, and ends up being easier to reason about. The same concept applies to functions, too!

A common pattern is to have a function that does some work and then calls another function, which does some work and calls another function, and so on. The problem is that the first function then cannot be used or tested without the entire chain of other functions it calls. This is the function equivalent of "inheritance."

Instead, we can compose functions, that is, pipe them together. Instead, take the output of the first function and pass it to the second, then take the second's output and pass it to the third, etc. That way, each of the functions can be reused, tested, and understood in isolation, then we can stick them together like LEGO blocks to build whatever series of steps we want.

That is, instead of this:

<?php
function A($in)
{
  
// ...
  
return B($out);
}

function

B($in)
{
  
// ...
  
return C($out);
}

function

C($in)
{
  
// ...
  
return $out;
}
?>

Structure it like this:

<?php
function A($in)
{
   
// ...
   
return $out;
}

function

B($in)
{
   
// ...
   
return $out;
}

function

C($in)
{
   
// ...
   
return $out;
}

function

doit($in) {
   
$out = A($in);
   
$out = B($out);
   
$out = C($out);
    return
$out;
}
?>

Now `A()`, `B()`, and `C()` are all easier to read, understand, and test, and we can more easily add a step B2 or D if we want. So powerful is this concept that many languages have a native operator for piping functions together like that. PHP doesn't, yet, but it's straightforward enough to do in user space anyway.


Want to know more about functional programming and PHP? Read the whole book on the topic: Thinking Functionally in PHP.


Thinking Functionally in PHP

Byte-sized functional programming: Filter first

Submitted by Larry on 3 August 2020 - 1:47pm

Often when working with a list, we only want to work with a subset of a list that meets some criteria. All non-zero values, for example, or all users that have a given role. The procedural way to do that is to stick an if statement inside a foreach loop:

<?php
foreach ($list as $value) {
    If (!
meets_criteria($value)) {
        continue;
    }
   
// ...
}
?>

That mixes up the filtering with the iteration, though. It also doesn't work if we're using `array_map()`.

Instead, we can make stripping down the list a separate operation called "filter." PHP offers the array_filter() function for that purpose.

<?php
$criteria
= fn(User $user): bool => $user->hasRole('moderator');

$filtered = array_filter($users, $criteria);
?>

Now we can work with the `$filtered` list, which has only the values we want. That could be a simple foreach<code> loop, or, better, it's now ideally suited for use with <code>array_map().


Want to know more about functional programming and PHP? Read the whole book on the topic: Thinking Functionally in PHP.


Thinking Functionally in PHP

Byte-sized functional programming: Mapping out your data

Submitted by Larry on 27 July 2020 - 3:42pm

Procedural code tends to think in terms of writing out steps, and so the usual way to work with a list is to iterate it using a `for` or `foreach` loop.

Functional code tends to think in terms of the relationships and transformations between data, where those relationships and transformations are defined as functions. That means the natural way to work with a list is to define a function that is the relationship between one list and another. The most common way of doing that is with a "map," which in PHP usually means the `array_map()` function.

With `array_map()`, you give it an array and a function. You get back the result of applying that function to every element in the array, individually. Like so:

<?php
$arr
= [1, 2, 3, 4, 5];
$fun = fn($x) => $x * 2;
$result = array_map($fn, $arr);
?>

The advantages of that over a `foreach` loop are:

* The function is a separate operation that can be as complex as you want.
* If it's more than a line or two, make it its own function or method somewhere and test it in isolation.
* If it's trivial, you can simply inline it.
* It's clear, visually, that every element's transformation is independent of every other's, because that's how `array_map()` works. A `foreach` loop may maintain state from one iteration to the next, but `array_map()` does not.


Want to know more about functional programming and PHP? Read the whole book on the topic: Thinking Functionally in PHP.


Thinking Functionally in PHP

Byte-sized functional programming: Immutable variables are easier to understand

Submitted by Larry on 21 July 2020 - 7:30am

An immutable variable is one whose value doesn't change after it has been first set. PHP doesn't natively support that, but we can write our classes in such a way to simulate it. For example, rather than this:

<?php
class Point
{
   private
int $x;
   private
int $y;
 
   public function
__construct(int $x, int $y)
   {
      
$this->x = $x;
      
$this->y = $y;
   }
 
   public function
moveUp(int $by): void
  
{
      
$this->y += $by;
   }
}
?>

We can write this:

<?php
class Point
{
   private
int $x;
   private
int $y;
 
   public function
__construct(int $x, int $y)
   {
      
$this->x = $x;
      
$this->y = $y;
   }

   public function

moveUp(int $by): Point
  
{
      
$new = clone ($this);
      
$new->y += $by;
       return
$new;
   }
}
?>

In the second version, once a given `Point` object is created it will never change. We can safely pass it to another function or method and be guaranteed that its value won't change without us knowing. Instead, any attempt to change it results in a new object, with its own identity, representing the new point in space. (In practice there would be other methods here as well, but we're focusing on just the mutation part.)

Code that uses immutable variables is easier to think about, because we don't have to worry about "does passing this object to this function change it?" We know it doesn't. Once we know something about an object we can guarantee that fact doesn't change. That can make a lot of subtle bugs impossible, which means we don't have to spend time looking for or correcting them.


Want to know more about functional programming and PHP? Read the whole book on the topic: Thinking Functionally in PHP.


Thinking Functionally in PHP

Advice for new speakers

Submitted by Larry on 18 July 2020 - 5:18pm

Someone messaged me recently to say he had just been selected for his first-ever conference talk, and since the talks of mine he'd seen in the past were so inspiring he wanted to know if I had any advice for new speakers. Since flattery will often get you somewhere, I offered the following advice. I figure it's generic enough that I should share it more widely. :-)

Continue reading this post on PeakD.

Byte-sized functional programming: Pure functions make testing easy

Submitted by Larry on 14 July 2020 - 9:28am

When testing stateful code, you have to first set up all of the explicit and implicit state that a given piece of code depends on. If the object you're testing has many dependencies that could be complex, or references global variables, or calls many other routines with their own dependencies, you may be looking at a lot of complex setup that makes testing hard to do effectively.

Pure functions greatly reduce that problem.

A pure function is a function that:

  • has no inputs other than those explicitly specified;
  • has no effect on any value other than the value it returns.

That means there is, by definition, only one place to inject dependencies: the call itself. And there's only one effect to validate: the return value of the function. No need to call a function and check what the effect was on some other value or piece of code: by definition, the only effect is the return value.

If one of the parameters you pass in is itself complex, that may make the test complex. But if the parameter is complex, that can serve as an impetus to simplify it. Rather than passing in an object, for example, just pass in a single function. If that function in practice has more complexity behind it, fine, but it makes passing a mock function trivial. Just... make a new (anonymous) function for the one test.

When your code has fewer sneaky interactions, there's less effort involved in testing as well as fewer things to test.


Want to know more about functional programming and PHP? Read the whole book on the topic: Thinking Functionally in PHP.


Thinking Functionally in PHP

Byte-sized functional programming: Pure functions encourage small code

Submitted by Larry on 6 July 2020 - 5:13pm

One of the many pieces of advice for a long-term sustainable code base is to keep code small. The larger a code base is, the more effort it takes to understand all the moving parts. Your brain can only keep so much mental model of your code in it at once, and if the code you're looking at is too large then what you can fit in your own "active memory" at once then you will have an increasingly hard time understanding it.

Most useful applications tend to grow larger than what the typical human can fit in their active memory, however, so you need a way to break up your code so you can load a relevant piece into your brain at once to understand and debug it. Usually that takes the form of encapsulation, coupling, cohesion, and other common object-oriented vernacular.

But what about just a pure function?

A pure function is a function that:

  • has no inputs other than those explicitly specified;
  • has no effect on any value other than the value it returns.

That has a number of advantages, such as being idempotent (calling it a second time with the same values is guaranteed to return the same result) and referential transparency (a function and its parameters is synonymous with its result, which can let you optimize the function away entirely in some cases). But perhaps the biggest advantage of pure functions doesn't have a fancy name: It's really easy to fit in your brain.

If you're trying to understand a given piece of code, a pure function will always be the easiest to understand because there is no need for context. There are some explicit inputs, which you can see; There is an explicit output, which you can see; And there's nothing else to care about. Reading the function (and the data definition of its parameters) is all you need to think about, because there's nothing else to think about. Every function becomes a natural "small enough to fit in your brain" unit.

A pure function will often call other pure functions, but that only creates slightly more overhead. Once you know a function is pure, it's easy to mentally "unload" as a black box that you can deal with separately. Go read that code first, then put it out of your brain and focus on the next function.

Moreover, functional programming-style code tends to favor function composition over direct function calls. That is, rather than function A calling function B which calls function C, you call function A and pass its return value to function B, then pass B's return value to C. That whole process can be wrapped up into another function if necessary. That makes it even easier to focus on only one function at a time, which is virtually guaranteed to fit in your brain at once.


Want to know more about functional programming and PHP? Read the whole book on the topic: Thinking Functionally in PHP.


Thinking Functionally in PHP

A Major Event in PHP ebook now available

Submitted by Larry on 6 July 2020 - 5:12pm

In early 2019, the PHP Framework Interoperability Group (PHP-FIG) released PSR-14, the Event Dispatcher specification. At the time I posted a long series of blog posts detailing PSR-14 in all its glory.

After discussing with a few other FIG folks, I've decided to release that blog series as a small ebook. Mainly that is to provide an easy single-point-of-reference for those who want to really understand PSR-14. Also, it serves as a simple fundraiser.

The book itself is available completely free, as the original blog posts were. However, you can also purchase a copy if you want to help support my work on the Framework Interoperability Group and Open Sourcing Mental Illness. OSMI is a non-profit organization that works to raise awareness of and research information about mental health challenges in the tech community. 50% of all royalties for this book are automatically donated to OSMI to support their vital work.

If you just want to grab a copy for free, go for it. If you are able to, though, I would encourage you to pay what you're comfortable with to support both my Open Source efforts and OSMI.

(This announcement is a bit late due to a publishing goof on my blog. If you're seeing this for the second time, my apologies.)