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:  christ...@quelltextlich.at
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 );

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Wikitech-l mailing list
Wikitech-l@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/wikitech-l

Reply via email to