PHP, minimalism and Occam's razor
Published: 01 December 2008Occam's razor is a principle that upholds simplicity. In a pragmatic manner, it can be interpreted as advocating that the simplest solution is best. This principle, historically attributed to William of Ockham (a real village in Surrey, and hence the name Occam's Razor) is often extensible to minimalism in software design and related decisions. Occam's razor is widely, and albeit implicitly, used in almost all scientific and engineering endeavors as a tool to scope and analyse complex systems, to 'shave off' the frills, noise and variables that are are irrelevant to the proper visualisation of the context in the pursuit of forming the best solution for a defined objective. The idea of decomposition with object-orientation is a kind of razor for software engineering. Building on this principle is the philosophy of software minimalism which adds an extra dimension by requiring that software be built to conform to its role while striving to consume the least resource in its execution
Occam's Razor: "Entia non sunt multiplicanda praeter necessitatem"
William tells us that it's not just simpler, it's better as well. Those Latin exhortations can be well read as "Entities should not be multiplied unnecessarily". In less cryptic form for this case, and from the presumption that the statement states a fact to achieve some form of goodness or perfection, we should interpret William's intended implicit message: when you have two paths that achieve similar objectives, the simpler one will be better. So, to see Occam's razor in action, Let's take a look at some code. Run the following code and observe the output:
<?php
class Occam extends ImagickDraw
{
private $_response = array();
private $_regression_coefficients = array();
private $_order = 0;
public function __construct($response)
{
$this->_response = $response;
$this->drawAxes();
}
public function coefficient($order)
{
$this->_order = $order;
$b = array_fill(0, $order + 1, 0);
for ($i = 0; $i < $order + 1; $i++)
$a[] = array_fill(0, $order + 1, 0);
$square = create_function('&$y', '$y = $y*$y;');
$b[0] = array_sum($this->_response);
$squared_response = $this->_response;
array_walk($squared_response, $square);
$ysquare = array_sum($squared_response);
for ($i = 1; $i < sizeof($this->_response); $i++) {
$xpower = 1;
for ($j = 0; $j < $order + 1; $j++) {
$term[$j] = $xpower;
$a[0][$j]+= $xpower;
$xpower = $xpower * $i;
}
for ($j = 1; $j < $order + 1; $j++) {
$b[$j]+= $this->_response[$i] * $term[$j];
for ($k = 0; $k < $order + 1; $k++)
$a[$j][$k]+= $term[$j] * $term[$k];
}
}
$this->_regression_coefficients = $this->gaussian($a, $b);
return $this;
}
public function gaussian($a, $b)
{
$ib = array_fill(0, sizeof($b), 0);
$n = sizeof($ib);
for ($i = 0; $i < sizeof($a); $i++)
$ia[] = array_fill(0, sizeof($a), 0);
for ($i = 0; $i < sizeof($a); $i++) {
for ($j = 0; $j < sizeof($a[$i]); $j++)
$ia[$i][$j] = $a[$i][$j];
$ib[$i] = $b[$i];
}
for ($j = 0; $j < $n - 1; $j++) {
$pivot = $ia[$j][$j];
for ($i = $j + 1; $i < $n; $i++) {
$mult = $ia[$i][$j] / $pivot;
for ($k = $j + 1; $k < $n; $k++)
$ia[$i][$k] = $ia[$i][$k] - $mult * $ia[$j][$k];
$ib[$i] = $ib[$i] - $mult * $ib[$j];
}
}
$coefficient[$n - 1] = $ib[$n - 1] / $ia[$n - 1][$n - 1];
for ($i = $n - 2; $i >= 0; $i--) {
$top = $ib[$i];
for ($k = $i + 1; $k < $n; $k++)
$top = $top - $ia[$i][$k] * $coefficient[$k];
$coefficient[$i] = $top / $ia[$i][$i];
}
return $coefficient;
}
public function drawAxes()
{
$this->line(10, 10, 389, 10);
$this->line(10, 10, 10, 359);
$this->annotation(5, 10, '0');
$this->annotation(230, 10, '10');
$this->annotation(0, 335, '2');
$this->annotation(0, 345, '5');
$this->annotation(180, 50, 's = simple');
$this->annotation(180, 60, 'c = complex');
}
public function reset($response)
{
$this->_response = $response;
return $this;
}
public function response()
{
foreach($this->_response as $x => & $y) {
if ($x != 0) {
$y = $this->_regression_coefficients[0] + ($this->_regression_coefficients[1] * $x);
if (isset($this->_regression_coefficients[2]))
$y-= $this->_regression_coefficients[2] * ($x * $x);
}
}
return $this;
}
public function plot()
{
$label = 'o';
if ($this->_order == 1)
$label = 's';
else if ($this->_order == 2)
$label = 'c';
foreach ($this->_response as $x => $y) {
$x*= 25;
$y*= 15;
if ($x != 0)
$this->annotation($x, $y, $label);
}
}
}
/**
* Create an array of x (key) and y (value) pairs of observations
*/
$observations = array(0, 11, 13, 15, 15, 16, 18, 21, 22, 22, 24);
/**
* Instantiate an Occam object with the specified observations
*/
$occam = new Occam($observations);
/**
* Plot the current state of the pairs according to their corresponding values
*/
$occam->plot();
/**
* Simple Case:
* Obtain the regression coefficients for a linear regression model to fit
* the initial observation
* Use the regression coefficients to calculate the value of Y given X,
* keeping the value of Y in memory
* Plot the current state of the pairs according to their corresponding values
*/
$occam->coefficient(1)->response()->plot();
/**
* Complex Case:
* Reset observations to initial state
* Obtain the regression coefficients for a 2nd order polynomial regression
* model to fit observation
* Use the regression coefficients to calculate the value of Y given X,
* keeping the value of Y in memory
* Plot the current state of the pairs according to their corresponding values
*/
$occam->reset($observations)->coefficient(2)->response()->plot();
/**
* Creates canvas, draw the graph and output of both cases.
*/
$canvas = new Imagick();
$canvas->newImage(250, 500, new ImagickPixel("white"));
$canvas->setImageFormat("png");
$canvas->drawImage($occam);
header("Content-Type: image/png");
echo $canvas;
?>
This piece, coded in PHP from a mathematical version shows why a simpler solution should be chosen. In this case, notice how both the simple and complex solutions produce very similar result. Additional complexity in the form of the third variable introduced in the second order polynomial model does not bring about greater visible perfection but in fact, has not much significance at all (the p-value is actually very high). Sometimes, additional complexity, such as the third variable, is introduced as frills in products. In more pragmatic manifestations, it could be a Sidebar, an authentication step, or a method interface. Hence, developing new features, or evolving versions, introducing increments to complexity beyond the core function may somehow reduce quality, usability, performance, etc. Creative destruction needs to be weighted and composed upon context. Thus, simplicity should be encouraged by introducing minimalistic perspectives in the creative process.
Now, consider web application development languages. PHP by far is one of the simplest languages that has become commercially popular, and it usually resides in a straightforward ecosystem that preserves elegant minimalism. Even with frameworks, performance monitoring and scalability tools added on, it's still simple if compared to some other Goliaths that preserve antiquated legacies that clearly involve expensive workarounds and heavy layers to deliver the web. So,for web apps, it seems the guy from Ockham says PHP is better.