The author wrote „Higher Order Perl“ which — to my knowledge — is one of the best books on programming ever written. While it’s got Perl in its title it is really an exhaustive introduction to the functional programming paradigm while being refreshingly non-academic about the subject. BTW: In general, I think there were many great Perl book authors having written very insightful introductory text books. They were mostly fun too read and taught me more about computer science than most computer science text books I had access too.
Absolutely. The Perl library of the early 2000s stands head and shoulders above anything I've read since in programming. O'Reiily blazed a trail with their Perl library because Perl seemed to attract the brightest minds and the best writers. I think Larry Wall's background in linguistics played a part. I have a more or less complete set of Perl books from the early 2000s which I frequently dip into for inspiration. "Perl Best Practices" by The Damian is a particular favourite. "Programming Perl" and "Mastering Regular Expressions" are timeless masterpieces.
Can confirm. I have just started to re-read a few chapters (about Regexes and Unicode) of "Programming Perl" (4th edition from 2012) and "Mastering Algorithms with Perl" from 1999, and these books are really a joy to read.
Witty language, and just the right amount of complexity.
Although the number of pages is high, these books were produced with incredible
attention to detail.
as it is a great introduction of functional programming and goes into all the gory details of how to implement lazy evaluation on top of closures.
Damian Conway: "Object Oriented Perl"
Comes to my mind as well. It's basically an introduction to object oriented programming so kind of similar in spirit to "Higher-Order Perl". It even talks about implementation techniques of OO protocols. Neat stuff.
These books are great even today as they transcend the Perl language.
A little outdated but still great to read are:
Randal L. Schwartz "Learning Perl"
Randal L. Schwartz "Intermediate Perl"
Brian D Foy "Mastering Perl" (my personal favorite in this series)
if you need to learn Perl 5 today.
I personally also liked
Tom Christiansen, Nathan Torkington: "Perl Cookbook"
which was basically just a large collection of programming solution recipes but usually also contained elaborate discussions on the various pros and cons of the corresponding solutions. I think it was in this book I first learned about the Fisher–Yates shuffle algorithm.
Brian D Foy: "Learning Perl 6" is a great book about Raku and is a more recent example of the great heritage of high quality introductory books about programming in the Perl domain.
(Nat Torkington here) Thanks for the kind words about Perl Cookbook! I had fun writing it and learned an enormous amount from Tom. The philosophy was that we show you the possible answers, tell you when each is appropriate, and then tell you how and why they work. You come for quick answers and before you know it, you've learned something! Every time I learn a new language, I wish someone had written a good cookbook for it.
Wow, thanks so much for your work! I had it on my desk for a long time because it was so useful. You are right every language should have a book like this!
It’s a good way to learn from because in practice you have these messy problems you’ll sometimes have to tackle from various sides before you push for a solution. Also software development is not about knowing all algorithms by heart but knowing when to reach for established work that is bullet proof and will save you and your organization a lot of headache in the future.
I'd add Network Programming with Perl by Lincoln Stein.
Somewhat more specific, and many of the libraries used are dated. But it did provide you with a huge amount of information, from writing simple echo clients with raw sockets up to fully multi process HTTP deamons, in a very accessible way.
Wonderful books! They had so much wit and were fun to read. I really enjoyed reading those Perl books. Maybe they were in the spirit of the Perl founder, who was/is a linguist and did not just write technical books, but almost technical literature. Haven't seen these kind of in-between-the-line writing in Python-land (or did I miss the good ones?).
I also bought a huge amount of Perl books as they're basically free + postage as the supply is much higher than demand.
The best one I've come across so far is from the guy that goes by "Ovid" (Curtis Poe). It had the right amount about the language, environment, package managers, popular libraries...etc. I liked the code as well.
Damian Conway's book Perl Best Practices. And here's the reason why I thought of it:
at a previous job where we were stuck with a million lines of less-than-optimal perl code, and the original developers who only knew perl, we had to make a decision. Rewrite it in a more gentle language that suited our new developers too, or try to write better perl. Initially we went with better perl, we used a linter called perl critic that would refer you to specific sections of Damian Conway's book Perl Best Practices. We managed to write very nice code that pleased all the new FP enthousiasts present, and were able to onboard non perl developers (incl. myself) fairly easily. It didn't work out though. The problem? Our original perl developers were unable to work with our "flashy" new perl code. It might as well have been an entirely different language to them. As it often is, this was not a coding problem but a people problem.
This one is really good. I no longer work with Perl but it's one of the books that had the most impact on me. There are many things to take away from it no matter the language you code in.
I read Damian's Perl Best Practices book for insights into ways to improve my Perl coding, but since then I've found that the principles I picked up from it have significant improved my code in most other languages as well.
Randal Schwartz's Learning Perl (O'Reilly) also deserves mention here. It was fantastic--exactly what an introductory book for a smart audience should be. It had examples; it was informative and very clear; and it was succinct.
For the most recent editions of Learning Perl, Randal Schwartz has been joined by brian d foy and Tom Phoenix. I suspect that those editions are good, too, but I haven't checked them out.
It's instructive to compare Learning Perl, which was (I think) under 300 pages in its early editions, to Learning Python, which is nearly 1600 pages long in its fifth edition. There's a lot of value in the longer book---but these two books exemplify very different ideas about what an intro book should be.
Even the Perl error messages are great. I've worked in Python and C# since my years with Perl, and neither has error messages worth a damn compared to Perl's. Perl tries to tell you where you went wrong. The others just tell you what's wrong with them. It's ridiculous that it's the 2020s and learning a language still involves learning a compiler's error messages.
> A few years ago I gave a conference talk in which I asserted that the
C++ macro system blows goat dick. This remark has since become
somewhat notorious, and the C++ fans hate me for it. But I did not
think at the time that this would be controversial. I was sure that
even the most rabid C++ fans would agree with me that the C++ macro
system blows goat dick, for the reasons I have just described.
mjd rocks but 2005 was before I made custom keywords in perl "work" on CPAN, and even further before people who were competent to do so obsoleted my horrific proof of concept hack with perl core features in 5.14/5.16 - which have since allowed many cool things such as http://p3rl.org/Future::AsyncAwait
Lisp macros are, of course, still cool, and this is why I got into this sort of hackery in the first place.
I'd like to point out that the strange occurrences of the "=3D" string are due to the fact that the message was originally a "quoted-printable" encoded email and for whatever reason this artifact wasn't removed when posted to the web. Perhaps this was pipermail's fault?
Anyway, when reading the code, you're safe in assuming "=3D" is just a single plain old equal sign. In the "quoted-printable" encoding scheme the two characters following an equal sign are the hex value of the intended character. In effect, equal signs need to be escaped using the "=3D" sequence.
This drove me a little nuts as I was reading and trying to code switch between the assorted languages used in the examples.
Quite sad btw that many lisp book are old. And wonder if those Perl book can they be rewritten at least just GitHub source code only for study purpose.
He definitely used a significant amount of elbow grease on this project. I’m sure that it was at least a year’s worth of work for him, but he actually gave this same talk several times that year so I bet he came out ahead.
On the other hand, Regex::Debugger is something I really miss when I am working in any other language. I think Perl is unique in having a real debugger for regexes. The closest analogue is re-builder in Emacs, which lets you type a regex and see how it matches against a bunch of sample text. Very useful, but not the same as stepping through the regex.
Lisp macros are really cool. Programming is not satisfying to me without a similar feature.
But, most people are happy with just typing and copy pasting stuff around. They don't care. I've shown them before and after macro refactoring code comparisons and they just stare blankly, they don't see the point.
They code stuff like this on a daily basis:
Var stuff() As Stuff
stuff.Add(New Stuff("foo"))
stuff.Add(New Stuff("bar"))
stuff.Add(New Stuff("baz"))
Twenty years ago when I was trying to explain macros to fellow Java programmers I would say, "what if Java didn't have try/catch/finally, could you add it?" I guess in some theoretical way you could, but the answer is not really.
Then I would show them how to use a simple macro I had written for sql statements that would turn a block of statements into a transaction:
(sql-tran
(statements)
(commit)
(rollback))
I would point out that only the commit or rollback expressions would execute. It also generated symbols so that these could be nested.
At the end of the day, you get code that looks like:
(transfer-money amount from-account to-account)
and it really feels like cheating because it's all wrapped up in transactions and handles errors without having to see that splattered across the code.
Even as a beginner in Lisp I had more and easier code reuse than I've had in any language before or since.
There's a more abstract/meta-level strategy or philosophy at the heart of this issue that I feel I've progressively gotten more wisdom on. IMHO:
DRY has trade-offs like everything else. Code that is very new, maybe built to satisfy business requirements that are still fuzzy, and shifting around a lot, can result in perfectly DRY code returning a net negative cost/benefit.
As your requirements solidify and the code matures, the net benefit of DRY code increases, often at a super-linear rate.
Going back to your example, I'd say go ahead and copy and paste if and when that strategy fits the scenario, and with the consciousness that you're taking on planned tech debt.
I've found more success with DRY2x, that is, it's okay to repeat myself once, but when I run into something three times (second repeat), it's worth of some attention.
This isn't even a macro thing, it just needs trivial use of procedures/methods, and I too have worked with people that are fine copying much larger chunks of code around rather than make a new function, and I just don't understand it. It's neater, safer and faster to encapsulate it, and they still don't.
There are so many things I usually have to do before code organization and abstraction. Figure out how to go from A to B, find out if I really want B, find out if I need to pull this apart into more steps, try variations, read docs to see if there are things that might help me etc.
I find it almost always better to write dumb code like this until I it does what it needs doing. It's much more straight forward, in a very literal sense, to find suitable abstraction and compression from there. DRY is about knowledge representation, not about repetition or boilerplate. Solving the former first in a good way lays the foundation for solving the latter, which can then be done with more powerful abstraction tools such as macros.
Yeah, write unclever code then refactor. Unless you know the domain well don't try to get smart upfront. I do this repeatedly, pushing the abstraction layer higher on each rewrite. I rarely start out with the abstraction first. [Edit: as the abstraction starts to take shape, it feels 'right' and 'elegant' not at all forced]
> I think I read somewhere that the worst code comes from the wrong abstractions.
I'd say no, it comes from cut & paste :) seriously
> I find it almost always better to write dumb code like this until I it does what it needs doing
Same! Then I abstract. But others never get past cut & paste.
> DRY is about knowledge representation, not about repetition or boilerplate
err, isn't it exactly about not repeating yourself? I mean, for the ultimate aim of representing the thing better, don't repeat it everywhere, ergo encapsulate?
From the Pragmatic Programmer (coined/popularized the term DRY):
"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system"
To contrast: Data especially can be very repetitive, say you have a bunch of configurations of somewhat similar things, or you have records that represent purchases at a given time of a thing by someone, they will tend to look very similar, maybe even almost the same
DRY is not about reducing this kind of repetition, because this is just like it should be. Sure, you might want to generate/automate repetitive things or compress them into something more readable and so on, but that's not what DRY is about.
Ultimately it is a form of normalization and decoupling. Ideally you want your code to be in a state so when you change things there is as little coordination required as possible.
DRY really isn't about compressing the data on which your code operates. It's about reducing the number of times your code coordinates the same knowledge about those operations across its text. If you add a field to a record and have to edit the code more than a couple of places to deal with that, you're repeating yourself. If you repeat yourself, those different parts of the code are not only more work to understand and to change, but are more likely to diverge from one another as they are edited in slightly different directions.
It can be attributed to one of the following (non-exhaustive list): Lack of experience and knowledge, lack of motivation or work ethics, lack of time to make things neat, lack of care to write readable code, lack of understanding what elegant code is.
Macros are the wrong tool here (as in most situations). A macro will only expand to the same bad code. What you want is a function that can do everything what's done on a single line here. For slightly more complex cases, use a list to iterate over and call the function. That will replace (number of lines) function calls by a single one.
I ended up writing the equivalent of lisp macros in C++, of all languages.
One thing that helped greatly with that is the “defer” statement. It lets you defer a section of code to the end of the current scope. Which means you now have compile time access to the entire current scope.
We’re writing a compiler for custom ML hardware using MLIR. MLIR has the concept of a current insertion point when you’re building an AST.
emacs users will know where I’m going with this.
We now have a save_excursion macro which stores the current cursor, then resets it at the end of the scope automatically. So when you’re building a loop or an if statement, you normally have to call (pseudocode):
make loop
set cursor to loop body
make statements inside the loop
set cursor to “after loop body”
Now it’s
make loop
save_excursion();
make statements inside the loop
Notice that the work decreased by 25% (three lines instead of four). In C++ land especially, this is a big win.
The “make loop” part is actually so long that our linter breaks it onto several lines. But after making a macro for it, it looks like:
affine_for(loop, idx, 0, n, 1);
make statements inside the loop
And the statements in the loop can of course reference “idx”, the iteration variable, or “loop” (a first class object representing a for loop).
Boom, now the work is cut by 50%. Two lines of code. Technically it went from three to one, and it’s far more readable.
The usual lisp disciplinary rules apply. You need to be familiar with variable capture, and how to make variable names at compile time. (In C++ I use a UNIQ(x) macro, which gives me a unique name for x. You can capture it into a #define by making a second #define and passing UNIQ(x) into it. So in practice most macros like save_excursion become two #defines, since you have to reference the unique name of the variable to store the cursor.)
I don’t know if it’s common. I assume not. Most lispers are rightly allergic to C++. But most lispers also lose the benefits of working in a team setting — so in practice, porting as much benefit from lisp to other languages is the biggest win of all. And that requires you to learn lisp. (Interested readers should look up the Blub Paradox, then move on to studying On Lisp.)
I guess he doesn't really know the parts when lisp macros are uncool. He even had the breaking example in his slide. The x++ part also breaks in lisp and only works in scheme, or when you have to introduce gensym into your macro. scheme macros are the real thing, but are not as simple and homomorph as lisp macros. they are more like constexpr matchers. if only we would have structural matching in our languages. and then do that at compile-time.
Using gensyms for single evaluation is normal for experienced Common Lisp macro writers. If you find it awkward, you can always write a macro to make it look cleaner :-)
> One obvious advantage is that there hardly is any syntax. You can
learn enough Lisp syntax to write useful programs in about ten
minutes
Perl encourages idiomatic programming styles which make collaboration difficult. I'm the eng mgr of a team which writes (among other things) Perl for big data processing applications and none of our programmers understand code the others have written without extensive (measured in weeks rather than days) analysis. I've never encountered the problem to this extent on Java code bases.
I'd be happy to help solve that - email mdk@shadowcat.co.uk to get an NDÀ signed and I'd be delighted to spend a few hours gratis helping your team not have that problem anymore.
Code can be written in all programming languages that is hard for other programmers to understand (including the original programmer a few weeks later).
I find that Test Driven Development helps to tackle that issue as you get a set of tests that show what the code should be doing and the Refactor step reminds the programmer to restructure the code so that it's easier to understand.
I love the idea of Lisp, but every time I have a look at CL I feel it needs a fresh reboot without a lot of the... strange, ugly, or opaque stuff I guess. For example, some things are in-place, some aren't, and I'm usually running into strange problems that are difficult to debug. Granted, I'm a layman, but it makes learning quite difficult.