Hi Tim, on Mon, Mar 19, 2012 at 09:09:58AM +1100, Tim Starling wrote: > On 18/03/12 20:37, Christian Aistleitner wrote: > > Dear all, > > > > should we allow using PHP's assert [1] in MediaWiki code? > > > > It would allow us to formulate and automatically verify conditions > > about code, while at the same time providing readable documentation of > > code for free. > > > > Possible, exemplary use cases would be: > > - automatically verifyable documentation of code's intent > > - guarding against logic pitfalls like forgetting to set a variable in > > all branches of switches, if/else cascades > > - guarding against using uninitialized variables > > > > What do you think? > > We use exceptions for that.
Yes, this was the motivation for my email.
'If'-guards are fine. Just as exceptions are. They are excellent tools
for conditions that /typically/ hold at run-time--but eventually they
might fail. In such a case, we want to do classical error handling.
It's the right tool for the job.
We can of course decide to keep using if-guards/exceptions when
modelling conditions that /unconditionally and always/ hold.
However, PHP introduced asserts some 12 years back for just this and
only this use case. It's a proven tool.
assert is tailored for conditions that /unconditionally and
always/ hold. So why not allow this standard tool in our toolbox?
Due to this narrower use case, assert comes with some benefit over
if-guards/exceptions in terms of code readability and quality:
- We can turn off checking the conditions on production machines, to
lower the impact.
- assert's syntax shows the condition that holds. [1]
- asserts produce good error messages without condition duplication. [2]
- asserts clearly stand out in code. [3]
- asserts just add the bare necessities to the code and do not clutter
up code so much
- asserts are less code to write.
> > P.S.: For typical MediaWiki use cases, PHP's assert is even faster
> > than throwing exceptions behind 'if'-guards.
>
> That's funny, for me "if" is about 10 times faster than assert() in
> the non-throwing case.
Have you tried real world examples?
Consider for example
$this->isOpen() && $this->mConn
This is a typical condition one could add in many places of
DatabaseMysql.php. For this condition asserts are ~16% faster [4].
For this real-world example, the fact that assert takes the condition
as string (hence unevaluated) outperforms the penalty due to the
function call.
But speed is just in the "P.S.". assert's real benefit would be
improved readability, as pointed out above.
Kind regards,
Christian
[1] If guards show the negated condition. Hence, when reading the
code, you have to mentally negate the condition again before actually
knowing what has to hold.
[2] An
assert( 'condA && condB' );
would relate to
if ( ! condA || ! condB ) {
throw new MWException( 'condA && condB was violated' )
}
Hence, if e.g.: condA changes, asserts just changes condA and we are
done.
For if-guards/exceptions, we have to adapt both occurrences of
condA. This is somewhat error prone and it's easier for the conditions
to run apart.
[3] if-guards/exceptions look like normal code. Hence, you have to
mentally reparse it again and again and detect them. Typically IDEs
cannot help or highlight only those guards that document code.
IDEs can easily detect and understand asserts. Even REs can find them ;)
[4] Please verify the number yourself. It was obtained by the attached
assert_test.php. The output for me was:
RUNS: 10, ITERATIONS: 1000000
assert: 1.818
ifGuard: 2.148
assert: 1.798
ifGuard: 2.151
assert: 1.795
ifGuard: 2.162
assert: 1.798
ifGuard: 2.148
assert: 1.801
ifGuard: 2.154
assert: 1.800
ifGuard: 2.134
assert: 1.788
ifGuard: 2.140
assert: 1.790
ifGuard: 2.141
assert: 1.791
ifGuard: 2.146
assert: 1.797
ifGuard: 2.141
total: assert: 17.976
total: ifGuard: 21.464
assert is ~16% faster than ifGuard
--
---- quelltextlich e.U. ---- \\ ---- Christian Aistleitner ----
Companies' registry: 360296y in Linz
Christian Aistleitner
Gruendbergstrasze 65a Email: [email protected]
4040 Linz, Austria Phone: +43 732 / 26 95 63
Fax: +43 732 / 26 95 63
Homepage: http://quelltextlich.at/
---------------------------------------------------------------
<?php
define( "ITERATIONS", 1000000 );
define( "RUNS", 10 );
printf( "RUNS: %d, ITERATIONS: %d\n", RUNS, ITERATIONS );
class ClassA {
public $mConn;
public $mOpened;
function isOpen() {
return $this->mOpened;
}
function funcAssert() {
assert( '$this->isOpen() && $this->mConn' );
$this->mConn++;
}
function funcIfGuard() {
if ( ! ( $this->isOpen() && $this->mConn ) ) {
throw new ErrorException( '($this->isOpen() &&
$this->mConn) does not hold' );
}
$this->mConn++;
}
function testAssert()
{
for ($i = 1; $i <= ITERATIONS; $i++) {
$this->funcAssert();
}
}
function testIfGuard()
{
for ($i = 1; $i <= ITERATIONS; $i++) {
$this->funcIfGuard();
}
}
}
assert_options( ASSERT_ACTIVE, 0 );
$obj=new ClassA();
$assertTotal=0;
$ifGuardTotal=0;
for ( $j=0; $j < RUNS ; $j++ ) {
$obj->mOpened=true;
$obj->mConn=2;
$before=microtime(true);
$obj->testAssert();
$after=microtime(true);
printf("%10s: %04.3f\n", "assert", ($after-$before) );
$assertTotal+=($after-$before);
$obj->mOpened=true;
$obj->mConn=2;
$before=microtime(true);
$obj->testIfGuard();
$after=microtime(true);
printf("%10s: %04.3f\n", "ifGuard", ($after-$before) );
$ifGuardTotal+=($after-$before);
}
printf("total: %10s: %03.3f\n", "assert", $assertTotal);
printf("total: %10s: %03.3f\n", "ifGuard", $ifGuardTotal);
printf("assert is ~%d%% faster than ifGuard\n",
(1-($assertTotal/$ifGuardTotal))*100 );
signature.asc
Description: Digital signature
_______________________________________________ Wikitech-l mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/wikitech-l
