Re^2: Yet another config file editing programme : Tell me how to make it better ! (Switch/smartmatch References)
by eyepopslikeamosquito (Archbishop) on Sep 03, 2021 at 09:39 UTC
|
I started using Switch, then I read in CPAN "do not use if you can use given/when".
Then when I used given/when, I ended up with warnings plus advice not to use given/when. Hmmmmmm.
As an occasional perl user, that is a little frustrating rewriting the same section of code 3x to do exactly the same thing.
I just need something that has case type functionality.
I can certainly understand your frustration.
This was a very sad affair for the Perl community.
Though adding smart match into Perl was premature,
and had to be backed out, at least P5P learned a valuable lesson.
Not wanting to gloat (no, really) but this didn't affect me, at all, because I've never been a fan of switch.
Stronger, I've almost never used Switch in over 20 years of coding in C++ and Perl and always
queried its use during code reviews.
Though it's a bit extreme to call Switch a code smell, cleaner alternatives, such as lookup-tables (hashes in Perl)
and polymorphism (in OO languages, such as C++ and Java) should be preferred.
From Perl Best Practices I suggest you take a look at
the Control Structures chapter, especially:
- 6.16 Value Switches - Use table look-up in preference to cascaded equality tests (item 78)
- 6.17 Tabular Ternaries - When producing a value, use tabular ternaries (item 79)
Though my advice in Perl is usually "just use a hash",
as a last resort you could replace your switch with an if-elsif-elsif-else construct.
References Added Later
On CPAN:
- Switch by Alexandr Ciornii (chorny?) - a switch statement for Perl, do not use if you can use given/when
- Switch::Plain by Lukas Mai - a simple switch statement for Perl (not a source filter, uses pluggable keywords)
| [reply] [d/l] [select] |
Re^2: Yet another config file editing programme : Tell me how to make it better !
by eyepopslikeamosquito (Archbishop) on Sep 03, 2021 at 01:25 UTC
|
There is a very small, but finite risk of the Pi being turned off in the middle of a file write
Ha ha, I have long experience contemplating this annoying and tricky problem!
The straightforward solution I concocted back in 2003 (and am still happy with) is to simply write a new file on the same file system ...
and then use (atomic) rename to clobber the original file - but only after the new file
has been successfully written.
This is described in detail at:
| [reply] |
|
| [reply] |
|
From Path::Tiny's doco of its excellently named spew method: "The file is written to a temporary file in the same directory, then renamed over the original.". Hey, xdg stole my idea! Seriously, it looks like an excellent module, I should start using it ... especially given my old golfing buddy endorses it. :)
| [reply] [d/l] |
|
Hi
I am going to use Path::Tiny.
| [reply] |
Re^2: Yet another config file editing programme : Tell me how to make it better !
by tybalt89 (Monsignor) on Sep 03, 2021 at 09:41 UTC
|
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11136353
use warnings;
use Path::Tiny;
my $dhcpcdfile = 'fake.353'; # FIXME filename
-w $dhcpcdfile or die "cannot write $dhcpcdfile";
my %ip_params = (
interface => "usb0",
ip_address => "1.1.1.0",
routers => "127.0.0.0",
domain_servers => "1.1.1.1"
);
%ip_params = ( %ip_params, # add new data to defaults
path('inputfile.353')->slurp =~ /^(\w+)=(.*?)\s*$/gm ); # FIXME file
+name
my $foundinterface = 0;
path( $dhcpcdfile )->edit_lines( sub
{
if( /^\s*profile\s+static_$ip_params{interface}\b.*\n/m ) # alter th
+is section
{
$foundinterface = 1;
}
elsif( $foundinterface and /^\s*static/m )
{
s/^\s*static\s+(\w+)=\K.*/$ip_params{$1}/m or die "failed to chang
+e $1";
}
elsif( $foundinterface )
{
$foundinterface = 0;
}
} );
| [reply] [d/l] |
Re^2: Yet another config file editing programme : Tell me how to make it better !
by hippo (Bishop) on Sep 03, 2021 at 09:43 UTC
|
I started using Switch, then I read in CPAN "do not use if you can use given/when". Then when I used given/when, I ended up with warnings plus advice not to use given/when. Hmmmmmm. As an occasional perl user, that is a little frustrating rewriting the same section of code 3x to do exactly the same thing.
I completely agree and understand. The (mis-)management of the switch/case equivalent in Perl over the years has become something of a lesson in how not to do it. given/when never struck me as a good idea so I just avoided it but plenty succumbed. Similarly, I never went anywhere near smartmatch but only a minority were enticed into that one. FWIW the current guidance is here. It will probably change again.
Sometimes being behind the curve is a good thing. These days I usually write Perl which is something like 5 years behind the current release in terms of features. This gives enough lead-in to be able to ascertain which "new" features have turned out to be turkeys and should be avoided. Such an approach is not for everyone, of course, but it can have its advantages (not least of which is back-compatibility).
| [reply] |
Re^2: Yet another config file editing programme : Tell me how to make it better !
by tybalt89 (Monsignor) on Sep 03, 2021 at 17:49 UTC
|
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11136353
use warnings;
use Path::Tiny;
my $dhcpcdfile = 'fake.353'; # FIXME filename
my %ip_params = ( # defaults
interface => "usb0",
ip_address => "1.1.1.0",
routers => "127.0.0.0",
domain_servers => "1.1.1.1"
);
%ip_params = ( %ip_params, # add new data to defaults
path('inputfile.353')->slurp =~ /^(\w+)=(.*?)\s*$/gm ); # FIXME file
+name
{ # block for local
local $/ = ''; # paragraph mode
path( $dhcpcdfile )->edit_lines( sub
{
if( /^\s*profile\s+static_$ip_params{interface}\b.*\n/m ) # alter th
+is section
{
s/^\s*static\s+(\w+)=\K.*/$ip_params{$1}/gm;
}
} );
}
| [reply] [d/l] |
|
{ # block for local
local $/ = ''; # paragraph mode
path( $dhcpcdfile )->edit_lines( sub
{
if( /^\s*profile\s+static_$ip_params{interface}\b.*\n/m ) # alter th
+is section
{
s/^\s*static\s+(\w+)=\K.*/$ip_params{$1}/gm;
}
} );
}
To an experienced perl programmer with deep knowledge of the language, this might look readable but for an occasional unprofessional perl user like myself, it would probably take at least half an hour to figure out what it is doing.
If I came back to this sort of code in a couple of years to alter/reuse it, I'd be back to square one.
If I had a future application that required a different input, this sort of all-in-one read/write approach would be difficult for me to repurpose.
It seems that brevity and obfuscation in perl code are inseparable.
For me, readability and brevity would be discovering a module Unix::ConfigFile::DHCPCD, that included a method "UpdateInterfaceIP". I think that a valid metric for a modern language is the code I don't have to write.
Please note I am not criticizing the skill or helpfulness those that give up their own time to write replies to people like me. I don't want to sound ungrateful. This is my goto place to find expert advice on perl, but claiming code is "readable" on a website that has a section devoted to "Obfuscation" is not a good look.
| [reply] [d/l] |
|
dazz, let me preface my reply by quoting Larry Wall from Programming Perl.
You can use Perl however you see fit ...
People feel like they can be creative in Perl because they have freedom of expression:
they get to choose what to optimize for, whether that's computer speed or programmer speed,
verbosity or concisenness, readability or maintainability or reusability or portability or learnability or teachability.
You can even optimize for obscurity, if you're entering an Obfuscated Perl Contest.
Any level of language proficiency is acceptable in Perl culture.
We won't send the language police after you.
A Perl script is "correct" if it gets the job done before your boss fires you.
You've made it clear you're an occasional Perl programmer who values readability highly.
I'm further assuming that you code Perl alone (rather than in a team) and that you're
trying to "get your job done before your boss fires you". Is that right?
I'm fine with that BTW.
As I'm sure you're aware, the monks who responded to your thread probably use Perl in a
very different work environment and participate here for different reasons.
You may have noticed, for example, the light-hearted tone tybalt89 used around readability vs brevity.
This is because he's renowned around these parts for writing very clever and very terse Perl code.
He will comment further if he wishes, but I'm guessing he participates here mainly for enjoyment ...
and he really enjoys writing clever and terse code!
As you might have guessed, I value clean and efficient code at work, code that must be
maintained by teams of many different programmers over periods of many years ...
while also enjoying recreational Perl (e.g. obfu and golf) as a pastime ... hmmmm,
maybe I have a personality disorder. :)
The many links at my home node will give you more detail,
in case you're interested.
I confess I pulled a face the instant I set eyes on your:
if ( $isFoundInterface == true )
Though this is "readable" to you (and I'm fine with that, TMTOWTDI is part of Perl culture),
if anyone in my team presented this at a code review meeting, we'd all be checking
our phones to see if it was April the first. That is because in my environment, code must
be maintained for many years by many different programmers, so we need to stick to the
programming mainstream, individualistic eccentricities like this would never pass code review.
Update: Put another way, in my work environment, Maintainability is more important than Readability (see Readability vs Maintainability).
| [reply] [d/l] |
|
|
|
|
|
|
|
It seems that brevity and obfuscation in perl code are inseparable.
Not at all. Obfuscation and abbreviation are different concepts.
There is code in here and out there which is highly obfuscated but quite long, perhaps the most famous example being camel code. OTOH, there is perfectly readable code of unsurpassed succinctness and brevity, on CPAN as well as lurking here in the dungeons.
Problem is, brevity encapsulates concepts, quite like in mathematics. The concepts behind a Ricci tensor or a Christoffel symbol easily fill books. But these weren't invented for obfuscation, but abbreviation. You need to know the symbols, their meanings and the operations they allow in order to calculate with these high order concepts.
One perl builtin construct is a fine example - the diamond operator. Consider (taken from perlop):
while (<>) {
# do something with $_ here
}
This is equivalent to
unshift(@ARGV, '-') unless @ARGV;
while ($ARGV = shift) {
open(ARGV, $ARGV);
while ($_ = readline(<ARGV>)) {
# do something with $_
}
}
So, the bare while(<>) { ... } is not an obfuscation, but an abbreviation for all the operations of its verbose version.
Or consider the following pieces of code, which do exactly the same thing:
%hash = ();
{
my @keys = qw ( a, b, c);
my @values = 1..3;
for ( my $c = 0; $c <= $#keys; $c++ ) {
$hash { $keys [$c] } = $values [ $c ];
}
}
@hash { qw (a, b, c) } = 1..3;
For me, readability and brevity would be discovering a module Unix::ConfigFile::DHCPCD, that included a method "UpdateInterfaceIP". I think that a valid metric for a modern language is the code I don't have to write.
Your expectation does not address readability and brevity, but language adoption and laziness. Perl does a very good job in doing as much for you as possible, as does CPAN, but it cannot by itself unite all different flavors of e.g. dhcp client configuration into a standard module. The maintainer of such a module would inherit all the technological debt of dhcp implementors. There are folks who try to do that, see https://www.webmin.com/. Download the https://download.webmin.com/download/modules/dhcpd.wbm.gz (which is a tar.gz) file, unpack it, and see what is needed for just the ISC DHCPD on various platforms. AFAIK they don't have support for DHCPCD client config, but feel free to submit a module.
It is the other way round: if all OS/platform vendors and DHCP implementors were to agree on perl as a standard configuration language, they would ship a Unix::ConfigFile::DHCPCD package which implements the method UpdateInterfaceIP whose body you would never look at, and wouldn't care about readability and brevity of its implementation, at all. But this ship has sailed far away and long ago.
Last point -
To an experienced perl programmer with deep knowledge of the language, this might look readable but for an occasional unprofessional perl user like myself, it would probably take at least half an hour to figure out what it is doing.
If I came back to this sort of code in a couple of years to alter/reuse it, I'd be back to square one.
Only use constructs you will never forget, and try to never forget what constructs you use.
Perl helps you with this, e.g. with the /x modifier for regular expressions. You could rewrite the tybalt89 code as
{ # block for local
local $/ = ''; # paragraph mode
path( $dhcpcdfile )->edit_lines(
sub {
if( m/ # match this:
^\s* # zero or more whitespace cha
+rs at line begin
profile # followed by the word "profi
+le"
\s+ # one or more whitespace
static_ # then "static_"
$ip_params{interface} # the value for key "interfac
+e" in %ip_params
\b # a word boundary
.* # zero or more following char
+s
\n # and a newline
/mx # in a multiline block
) {
# alter this section
s/ # match and substitute
^\s* # zero or more whitespace cha
+rs at line begin
static # word "static"
\s+ # one or more whitespace char
+s
(\w+) # a word (see perlre) capture
+d in $1
= # equal sign
\K # but keep what was matched s
+o far
.* # any following chars
/ # against
$ip_params{$1} # the value of key $1 (see ab
+ove) in hash %ip_params
/gmx; # globally in a multiline blo
+ck (for "x" see perlre)
} # endif
} # end of sub
);
} # end of "local $/" block
to explain what is meant today to the dumb-ass you'll be tomorrow. Just comment anything you might have forgotten later.
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
| [reply] [d/l] [select] |
|
It seems that brevity and obfuscation in perl code are inseparable
Further to shmem's insightful observation that obfuscation and abbreviation are different concepts,
I feel obliged to point out that composing works of deliberate obfuscation in programming languages is
a legitimate art form that long precedes Perl.
claiming code is "readable" on a website that has a section devoted to "Obfuscation" is not a good look
How about a section devoted to "Poetry"? ;)
To me, it's a great look. The Perl culture of having fun is what attracted me to Perl in the first place -
a refreshing oasis in the dreary corporate IBM/Microsoft desert that surrounded me twenty years ago.
Crafting compact, readable and well-documented production code for customers is a completely separate activity
to the artistic recreation of composing Perl obfus and poetry.
Please take a look at:
and let us know if you've changed your mind.
See Also
| [reply] |
|
|
Hi
I have working at writing better perl based on the advice received here but I am stuck.
I slurp in the configuration file I want to update OK.
I load it to ARGV to run it through a while( <> ) loop OK.
I search and edit the file line by line OK.
When I try and spew it, the file is blank.
The code sample below is not complete, and probably not working.
I have stripped out everything that I don't think is relevant to the slurp/process/spew problem.
I am wondering if the explicit use of $line is the problem??
I have tried removing all references to $line but no change. The output file is still blank.
#!/usr/bin/perl
use strict;
use warnings;
use Path::Tiny;
###### Output dhcp configuration file
# my $dhcpcdfile = '/etc/dhcpcd.conf';
my $dhcpcdfile = 'd.conf'; # TEST
####################################### Load the output config file
path($dhcpcdfile)->slurp; ###
#######################################
{ # block to limit scope
@ARGV = $dhcpcdfile;
while( my $line = <> ) { ### Is the explicit use of $line the pro
+blem ???
my @ip_fields = split( /=/,$line);
# look for profile with matching interface name
if ( $line =~ /^\s*profile\s+static_$ip_params{interface}\b.*\n/m
+ )
{ # format matches 'profile static_eth0'
}
elsif( $foundinterface and $line =~ /^.*static\s*ip_address=/ ) {
$line = "static ip_address=$ip_params{'ip_address'}\n";
}
elsif( $foundinterface ){
last; # No need to continue looking through the file
}
}
###########################################
path('spew.cfg')->spew(@ARGV); ### This saves a blank file
###########################################
}
| [reply] [d/l] |
|
|
|