For any project, create a virtualenv. Write and maintain a requirements.txt file. It will save your day many times.
It does not completely solve system-dependent problems, like TLS support (unless you run something like nix or guix).
(I prefer perl but this is a general principle and I agree)
If you use "my", Perl's scoping rules are better than those of Python. It's a shame that Python and Ruby are a step backward from the proper (Scheme-like) lexical scoping of idiomatic Perl 5.
The Perl community realized this, and introduced 'my'. The Javascript community realized this, and introduced 'let'. I'm not sure of the current status of Python, Ruby and PHP, which from my point of view all started out with rules broken in various ways...
1. The lifetime of a variable depends on control flow, and is not statically determined (in the general case). The exact scope is dynamic.
2. Many variables are not used throughout a function, but only within a small region of a function, e.g. an "else" block. Python's variables have a too large scope. To add confusion, a few Python constructs like "except" clauses or list comprehensions are block-scoped.
3. Working with nested functions is extra complicated. While you can read variables from outer scopes, you cannot assign them, unless you declare them as "nonlocal" or "global" in the nested function.
As a special case of 3, consider closures that are declared within a loop:
adders = []
for x in range(10):
adders.append(lambda y: x + y)
All these lambdas close over the same "x" variable which is reassigned. After the loop terminates, "x = 9" for all lambdas.Python's common workaround is to use an immediately invoked function that binds the value to a scoped argument. In lambda calculus terms, we have to desugar the "let" abstraction to function application:
...
adders.append((lambda x_: lambda y: x_ + y)(x))
(Another alternative is to capture by value by using default arguments, as the defaults are executed at function definition time: "lambda y, x=x: x + y".)A similar language that shares Python's problems is PHP, where a nested function has to explicitly "use" outer variables it wants to capture.
Perl (like most languages since Algol, Scheme, and C) requires explicit variable declaration (unless "use strict" is not enabled). In Perl, "my" declares a local/block-scoped variable. Consequences:
1. The lifetime of a variable is easy to determine statically: from the declaration to the end of the current scope (in Perl: to the next "}" that marks the end of the current block). This is called "lexical" scope.
2. I can restrict a variable to the smallest scope where it is needed, thus simplifying my code.
3. Nested functions are much easier. If I assign a variable, it uses existing (outer) bindings, unless I explicitly shadow the variable with a local declaration.
If we return to the closures in loops problem, Perl creates a new variable for each loop iteration, thus avoiding Python's problems:
my @adders;
for my $x (0..9) {
push @adders, sub ($y) { $x + $y };
}If a variable is assigned to somewhere in the function, even later in the function, or in an unreachable branch, accessing it will use the function's scope. If the variable doesn't have a value you get an UnboundLocalError.
Even after you del a variable, accessing it will raise an UnboundLocalError rather than look in another scope.
perlbrew
local::lib
Or, specify it when executing the module, ie: bash$ perl -I../project_modules/Module1/lib -I../project_modules/Module2/lib your_project.pl
Or, specify it at the top of your_project.pl ie: use lib '../project_modules/Module1/lib';
use lib '../project_modules/Module2/lib';
Or, set enviroment var export PERL5LIB=/home/project_modules/ ; perl your_project.pl
Or, make a BEGIN statement on top of your main project, ie: BEGIN {
push @INC, glob "/home/project_modules/*/lib";
}
You dont need to use anything global, and its better you dont. Never execute anything as root. You are able to execute local modules just like node, rvm, etc. Its all the same.Some linux installs come with perl/python installed. It is used by the system and is refered to as "system perl" or "system python" or "system ruby", etc.. and it is not recommended to make upgrade on those "system XXX". Only your system should upgrade the libraries it uses. That is why you should always use a local perl/python/ruby install.
If you didnt like it, its good you moved on.
I'm not a Python developer, just dabble, but I've had decent success with Pipenv[1] and Pipfiles.
That's fair, but Python definitely causes its share of confusion there as well. If anything, Perl's scoping (in modern perl5 with warnings and strict) is one of the nicest I've had the pleasure of working with in a scripting language: closures Just Work (tm), and having the choice of dynamic scopes when you need them is quite powerful.
Must have been Perl 3 or 4.
In Perl 5 you have to work extra to make global variables, and they're not really pushed in the global scope, only have global visibility in their own namespace ... unless you work even more and export them by default when you write the module they're in.
python's scoping is "invent a new variable in the current function when you mention the name"
since I actually prefer explicit to implicit, I prefer perl5 to python scoping-wise
So
foo = 12
No need to worry there's some global "foo" I overwrote (since it means knowing the entire program 10 lines or a million). That's actually one thing I think python got right over C/C++/C#/Java in that "foo=12" is always a local variable. It's never a member of the class. That's explicit as in "self.foo = 12". No ambiguity. JavaScript got that part right "this.foo = 12" but it got the other part wrong "foo=12" could be the global scope and there's no warning, just "you just crapped on the global namespace, good luck finding your bug" :( Fortunately modern tools like eslint catch that bug for me in JSAlso, you can use 'our' to restrict access to a global to a specific scope.
Also, javascript now has 'use strict' (stolen from perl5) which prevents that problem there too.
I fear you have been misinformed about both languages.
You can see the progression of perl from bad to tacked on workarounds. First "local" was added to manually copy values to some stack. Then "my" was added. But all the old problems are still there.
Then there's all the massive mess of $foo vs %foo vs @foo vs &foo. That's super cryptic and leads to really hard to parse code (IMO obviously).
And all the ridiculous cryptic globals https://www.perlmonks.org/?node_id=353259
No thank you
In Perl 6 declaration of any variable is obligatory by default.
> Then there's all the massive mess of $foo vs %foo vs @foo vs &foo
In Perl 6 that has all been made more systematic. Check out https://opensource.com/article/18/9/using-sigils-perl-6 if you want to know how Perl 6 differs from Perl 5 in that respect, and how you can actually have sigilless variables in Perl 6.
> And all the ridiculous cryptic globals
Perl 6 basically only has `$_` (the topic), `$/` (the current match result) and `$!` (the last error) as "cryptic" lexicals. See https://docs.perl6.org/language/5to6-perlvar for a list of Perl 5 variables -> Perl 6 functionality
Should you write your entire codebase in Perl, in 2018? hell no
But you probably shouldn't be writing anything more than 10-20 lines in Bash, either, and this class of organically evolving script, with little attention to design at the start (much like the language itself) is where Perl really shines.
Only problem with perl I've run into is analysing text at speed - 60,000 lines a second of regex doesn't work on a raspberry pi or other low end processor.
No magic required in Python at all for this. Just use virtualenv from your project directory (don't bother with vitrualenvwrapper).
$ virtualenv venv; . ./venv/bin/activatewith npm I have to go through special effort to get anything to install globally. With both python and perl I've read countless books and websites over the last 20 years and never once ran into that incantation. They all just say type
install Chocolate::Belgian
or cpan Chocolate::Belgian
or python -m pip install SomePackage
etc..There's a couple, but mostly just if you want to hack something, and usually there's another way to do it. They're useful for one-liners, but if you're doing it in production, you're usually doing it wrong.
Sure, you can use them, just like you can do some stupendously bad things in most languages. Perl does give you more options for that, but the trade-off is that you can often do quick testing or one-off processing from the command line with one liners.
E.g. Maybe you might run into $| (AKA $OUTPUT_AUTOFLUSH) or $/ (AKA $INPUT_RECORD_SEPARATOR) because those are useful and common. The rest are holdovers from awk. If you see them often in your code base, someone did you wrong.
> Crazy cryptic syntax.
I find it usually pretty straightforward, but that's how I write my Perl. The Perl I write these days is usually functions full of params with validation. E.g.
use Function::Parameters qw(:strict);
...
fun find_changesets_after(
DateTime $last_processed, # No default, so required
Maybe[DateTime] :$cutoff = undef, # :$ prefix denotes named variable
Bool :$debug = 0,
HashRef :$cache = {},
) {
# just use $last_processed, $cutoff, etc without instantiating them
...
}
# Called like so
find_changesets_after( $last, cutoff=>$not_after, cache=>$CACHE );
That's a bit on the complex side, but it's a far cry from what I was writing 20 years ago, and in a good way. The language is marching forward, sometimes through code advancements, sometimes through best practices modules (Moose, etc).> Ridiculous scoping rules with hacked on workarounds.
Err, what? The scoping rules are almost the same as C. There's some weirdness with $_, but don't go crazy with nested functions in maps and greps (and name your loop variables otherwise) and it's usually fine.
> When someone hands me a perl script I don't look forward to trying to get it to work.
Well, neither do I, because usually when I've been "handed a $LANG script" it's because it's problematic because it was written based on differing business requirements at a past time, or by by someone that was under extreme time constraints, or someone that didn't know what they were doing, or some combination thereof. So yeah, being handed a script can suck, just like being handed any codebase can suck.
If you were being handed scripts that you are just expected to run and use but aren't working, it sounds like someone didn't include or define the dependencies. If they did, there should be no problem, since a vanishingly small number of things could cause a Perl script written in the last two decades to not work anymore. Backwards compatibility is taken very seriously.
Based on the time period you're talking about, it sounds like you got handed a lot of hacked together CGI's and system scripts that were the glue for the beginning of the web. I feel for you, I got handed a lot of the same. Keep in mind a lot of it was written by Perl novices, extended by Perl novices, and then left to age ungracefully. Perl was everywhere in the late 90's, and a lot of beginning programmers were being thrust into tasks they didn't have a lot of experience for, so it makes sense that the average quality of a Perl program during this period would be very low.
> NPM might suck for various reasons but at least all deps are installed local to each project. I'm sure there are secret incantations for doing the same in perl and python but by default most stuff I download requires global libraries to be installed.
In Perl it's "cpan install $MODULENAME" or "cpanm $MODULENAME" (now) and it will download, compile (if required for bindings), test[1], and install all dependencies. Perl's had this done well for almost a couple decades too. For module authors, on uploading a module it's automatically spun out across a testing network and tested across a large matrix of Perl version, operating systems, and architectures, and the results are emailed to you (and visible online).[2] The cpan command was the appropriate way
There are valid criticisms of Perl, but I you have to set the bar pretty high to find major faults in CPAN (at least compared to contemporaries).
> If there is one thing I love about npm is that it defaults to project local installs and I have yet to run into a project that requires a global install
I've not run into a global module install problem in Perl in... well ever. It's supported local modules fine since I first started using it in the late 1990's.
If you're talking about interpreter installations, that use dto be much more painful (you had to actually install them and manage them manually), but since 2010 there's been perlbrew (inspired from the equivalent in Ruby or Python, I believe), which automates all this, allows multiple interpreters and libraries, etc. It sounds like that was past the time you were using the language, which is a shame, because it's really nice.
I think you'd be very surprised at the average level of Perl code these days. Part of that is a community that's not getting a huge amount of new blood, so the people that are left are mostly veterans, but that has some benefits at least, as few as they are.
1: And fail if the module or a dependency's tests fail (unless you specifically force it), as is right. If a module can't pass it's tests, you sure as hell don't want it 6 levels deep in a dependency chain acting oddly.
But ... more than any other language I've used it seems the most hacked and confusing. Global variables abound. Crazy cryptic syntax. Ridiculous scoping rules with hacked on workarounds.
I really don't get the continued love. Sometime around 2003 IIRC I got introduced to Python and never looked back. When someone hands me a perl script I don't look forward to trying to get it to work.
Speaking of which tho even Python now makes me crazy. NPM might suck for various reasons but at least all deps are installed local to each project. I'm sure there are secret incantations for doing the same in perl and python but by default most stuff I download requires global libraries to be installed. If there is one thing I love about npm is that it defaults to project local installs and I have yet to run into a project that requires a global install