I am convincing myself that fake programmers are not lazy. I think it applies to everyone that codes a piece of code such as:
$body =~ s/%FIRSTNAME%/$firstname/; $body =~ s/%LASTNAME%/$lastname/; $body =~ s/%RECEPIENT%/$recipient/; $body =~ s/%VERB%/$verb/; $body =~ s/%PRODUCT%/$product/;or
int foo = rs.getInt("foo"); String bar = rs.getString("bar"); Date xyz = rs.getDate("xyz");and do it over and over again. Abstractions like
# $body = process_t($templ, \%fields); sub process_t { # generic code which does # $body = $templ # $body =~ s/%\U$fields{$k}%/$fields{$k}/; # ... } # Map row = fetchRow( ResultSet rs ); Map fetchRow( ResultSet rs ) { # generic code which fills # a map with rs.getObject(col) # and returns it }impose themselves to me. They could be much longer that one round of "brute force" programming, but once written and tested, they will not let you down and, after a couple of uses, they will be shorter, faster and more reliable. In the end, probably fake programers are not proud or impatient as well.
Re:Abstractions aren't all good
ferreira on 2006-10-25T00:44:08
I agree with you that abstraction is not everybody's choice. But they are useful: the mistake in the abstraction would soon be revealed and fixed and then new bugs can be found, but not the same. On contrary, one can keep making mistakes with cut-and-paste and editing and fixing one would not help with the others. Writing such things in full seem to me not real laziness.
"Abstracted functions need better testing, and that can take longer." Yep, that's what I meant with "could be much longer that one round of 'brute force' programming, but once written and tested,
..." To be honest, I am very unhappy writing things in full, because I tend to be very careless and do the same errors repeatedly. That's why I like shorter solutions, even if they are more complex. By the way, what's the tiny mistake in the regex example? I only spotted:
$body =~ s/%RECEPIENT%/$recipient/; # RECIPIENTRe:Abstractions aren't all good
Abigail on 2006-10-25T08:19:13
Surely, the body of the subroutine should do the equivalent of:
body =~ s/%\U$k%/$fields{$k}/;Re:Abstractions aren't all good
ferreira on 2006-10-25T10:25:42
I said I was careless. Oh, there you are, filthy bug.Re:Abstractions aren't all good
Aristotle on 2006-10-25T13:43:27
Indirection is not Abstraction. Let’s stamp out this meme.
Re:Indirection is not Abstraction
DAxelrod on 2006-10-25T20:40:55
Thank you so much for that link.
The article helped convince me that the difference is that abstraction is designed to reduce complexity, whereas indirection is sometimes a neccessary way to implement abstraction. Indirection almost inherently involves *more* complexity, which is the reason that it is fundamentally different.Re:Indirection is not Abstraction
Aristotle on 2006-10-25T22:08:15
indirection is sometimes a neccessary way to implement abstraction.
Argh!
:-) No, that’s exactly what the rant is arguing against. Indirection is indirection. Indirection is not abstraction. Indirection trades simplicity for flexibility – it always increases complexity. Abstraction is the opposite direction: trading flexibility for simplicity. F.ex., Perl (largely) abstracts memory management away from the programmer.
Re:Indirection is not Abstraction
ferreira on 2006-10-26T00:15:47
[indirection] always increases complexity.Agreed, but abstraction may be combined with indirection and relieve some people from the additional complexity. Just to illustrate, I think DBI works a bit by indirection when it gives behavior for free to the database drivers (for example, fetchall_* methods) and when it delegates the specific bits to the drivers. It is sorta of abstract class and template methods. But that's happily transparent to the user. The complexity is there but relegated to the people who should care and need the power, the developers of DBI itself and DBD drivers.
Re:Indirection is not Abstraction
Aristotle on 2006-10-26T02:18:46
Yes. Zed makes this point in his article: you can sometimes make an interface both indirect as well as abstract. It just doesn’t usually work well, in which case you can (and, depending on the task, arguably should) wrap an abstract interface around the indirect one to reduce (some of the) undesirable complexity while retaining (some of the) the desirable flexibility.
In the case of DBI, doing both works well because it’s inherently a factory-style API: you specify connection parameters to create a DBH, then use the DBH to create STHs. That provides plenty of surface area for exposing the indirection abstractly: with a special DSN syntax, and by adding DBD-specific attributes to the generic objects you get. As a result, you never have to care about the DBD instance at all when you’re using DBI, it’s all under the covers.
Re:Indirection is not Abstraction
DAxelrod on 2006-10-30T18:21:11
What if the indirection is only an implementation detail?
Let's say we have a complex and messy interface to something. If we wish to wrap an abstract interface around it, our implementation gluing the abstract interface to the more complex one almost neccessarily includes indirection. The trick is not exposing the indirection to the users of the abstract interface.
I am not saying that indirection and abstraction are in any way the same thing. I am saying that they are often conflated because people use one while attempting to achieve the other.
Does this make sense, or am I still missing the point?Re:Indirection is not Abstraction
Aristotle on 2006-10-31T02:19:17
That does make more sense.
I don’t know if I agree with that explanation for the conflation, though. Perl-land isn’t that big on deep class hierarchies and indirection in interfaces anyway, and your claim that indirection is usually employed as a means on a quest for abstraction may well hold.
In contrast, indirection is almost a fetish in Java circles, where it’s rarely coupled with an abstraction. That’s what missing dynamism in the language does to you – forces you to reinvent it poorly…
I agree with Juerd, there are disadvantages of abstractions, although I have other reasons I sometimes don't abstract. And that's flexibility.
With the given code, it's easy to change one of cases, for instance I want to change it to:
Not impossible to do if you have abstracted it out, but harder, and easier to break unrelated things.s/%FIRSTNAME%/$firstname/;
s/%LASTNAME%|%CHRISTIANNAME%/$lastname/;
s/%RECEPIENT%/$recipient/;
s/%VERB%/$verb/i;
s/%PRODUCT%/$product/;
That's why I sometimes use cut-and-paste. Code that has been cut-and-pasted instead of abstracted out is easier to change without effecting the other. Whenever I have the choice between abstracting and cut-and-paste, I ask myself, what's more likely to happen in the future, that I want to change one of the paths, or both? Another question is ask is how much more complicated the abstracted code is.
Re:They aren't all good indeed.
ferreira on 2006-10-25T10:42:57
The extension you proposed to the example will surely make harder the abstracted code. But I may propose other extensions that could be harder if you have redundant code. For example, suppose you are generating HTML and so you want to escape every interpolated value. An only change in process_t would do.
$body =~/%\U$k%/html_escape $fields{value}/e; And many many changes in the original code. After you cutted-and-pasted, it becomes messier to apply significant changes to the copied bits to keep them synchronized in what they have in common and distinct in their differences. (And that's the main point which separates your example and this: single isolated changes are easier in cutted-and-pasted code, while global changes are often fast in abstracted code.)
Just for the record, to solve your extended problem, the template could be made more complex in the form { body => $body_template, map => \%aliased_placeholders_to_fieldnames } and the substitution would end similar to this:
which is not that complex, although most of the burden will be directed to build the map. In your example, { CHRISTIANNAME => 'lastname' } would be the additional data.$body =~ s/%\U$k%/$fields{$k}||$fields{$map{$k}}/e;