On Thu, Jan 02, 2020 at 07:49:28PM +0100, Marc Chantreux wrote:
> On Thu, Jan 02, 2020 at 10:42:54AM -0600, danieljb...@icloud.com wrote:
> > I don't understand why people say that perl's flexibility is a negative.
> 
> because sometimes, flexibility permit some endless sterile debates about
> the coding style.

Well, OpenBSD has got style(9). I have some specific adaptations for perl,
because a lot of the rules are for C.


Here are my current guidelines for OpenBSD perl tools.

In general, things are written following style(9) adapted for perl.

Specifically,
- named sub *are* functions.

So

sub f
{
        my ($arg1, $arg2) = @_;

        ... code

}

- three styles of parameter grab for methods:


sub m1
{
        my $self = shift;
}

No other parameter.

sub m2
{
        my ($self, $p1, $p2) = @_;
}

when getting all parameters (no check on the number usually)


sub m3
{
        my $self = shift;
        ...
        do_something_with(@_);
}

for functions with unlimited parameters after the first one

(dubious whether this changes anything for performance reasons)

- wantarray should *only* be used for optimization purposes (yes/no answer
instead of full list).   Doing otherwise utterly complicates matters.


- I almost never put extra parentheses, and use the "4 space indent" rule
for continuing statements.

- chained index lookups should ditch the extra ->  .
prefer $self->{a}{b}  to $self->{a}->{b}

- don't put quotes around indices unless absolutely necessary (keywords)
and don't use keywords for keys.


- anonymous subs are part of the code:
So:
        my $s = sub {
                my $self = shift;
                ...
                };


Note a full indent because the inside looks like code.

- modern perl prefered, so
    $value //= something;
prefered over
    $value = something  if !defined $value;

- autovivification welcome.

push @{$self->{list}}, value;
is perfectly fine without defining $self->{list} first.
Note that if (@{$self->{list} > 0)  *won't* autovivify list, so it can be
used for "does the list exist and is not empty" instead of 
if (exists $self->{list}


- I should probably normalize towards banning implicit return ?

- should I prefer "always refs"  over explicit % / @ ?
There is a slight legibility problem:
my @l;  is more readable than
my $l;  (this is a list)
and
my $l = [];   takes slightly more memory.


- most things unless explicitly being debugged should set
$DB::inhibit_exit = 1  right afer a fork and before an exec.


And I have some further general rules, learnt from past mistakes.

The perl package tools follow some stylistic and practical guidelines
- all new development should be object-oriented.
Have a package under either OpenBSD or DPB, and pass operations
to a constructed object (generally name the constructor new unless
you have better options) if you need to keep state, or to the
class name proper.

Examples:

my $pkgpath = DPB::PkgPath->new('devel/quirks'):

say "Normalized version is ",  $pkgpath->fullpkgpath;

$state->errsay(OpenBSD::Temp->last_error);

older code sometimes does not respect that.
It hasn't been converted because it's currently not worth it.
But there have been many instances where I've actually regretted
not doing things that way sooner.

The object itself is usually called "$self" unless there are reasons
not to.

Since there are no access control restrictions in perl, most often
internal methods are just prefixed with _.

Stylistically, methods without parameters don't need parameters, so
I don't write them, prefer $object->foo  to $object->foo()

It makes it less cumbersome to chain methods, e.g., $object->foo->bar(whatever);

- in the interest of chaining methods, stuff that tweaks an object should
return the object itself, so that

    $self->set_foo(1)->set_bar(2)->run

will actually work

- a lot of code creates "unique" objects.
The pattern is to have a %cache hash in the package, and have the normal
constructor do things under the radar, calling create as needed.
create won't normally be used by client code.

- a lot of code creates "just in time objects".
Error.pm containt the OpenBSD::Auto class, that can be used to create
jit values, it contains one single construct, cache, that is used like so:
OpenBSD::Auto::cache(solver,
    sub {
        require OpenBSD::Dependencies;
        return OpenBSD::Dependencies->new(shift);
    });

so that the first call to $self->solver(x)
will instantiate $self->{solver} to the required object.
And that call and all subsequent calls will return the same object.


- there are way less files than classes. Things are organized in a
"put a whole set of related things together in the same file".
Full OO also means you don't need to use Foo; from the start, you can 
require Foo; dynamically, thus loading it later.  This does speed up the
startup of tools significantly.

- in general, singletons are frowned upon. We still have a few (list ?),
mainly as cached values in specific packages.  There is some kind of global
state though, and that's generally handled through the $state object.


- DON'T USE MULTIPLE INHERITANCE unless absolutely necessary.
It's almost always a mess.  Grabbing some behavior from a class (mixin)
is best done on a per-method basis.
Know about &function;  (without the paren) which is a "goto that sub with
my @_ totally unchanged". So mix-ins can (and usually) do:

package B;

sub f
{
        &A::f;          # delegate to A
}

Reply via email to