All programming languages suck.
Nov. 10th, 2010 12:18 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
This post is brought to you by recent excursions in The Lacuna Expanse. You can probably skip this post and go straight there, unless you're a perl weenie. If you *are* a perl weenie, you may enjoy TLE. Not only is their API published and well documented, they encourage scripting and bot writing. You could, legitimately, write a bot to run your entire empire, and never log in again, and they'd be cool with that.
Anyway.
I have never been a fan of perl's map function.
There. I've said it. It confuses me, and makes me feel stupid. Partly, of course, this is self-fulfilling - I don't like it, so I don't use it, so I don't understand it when I see it, so I fear it, so I don't like it ....
Working in Lisp a bit recently has, oddly, increased both my understanding *and* my dislike. So I'd like to be educated.
Dear perl hackers. Does map give me *anything* other than syntactic sugar, or perhaps the opposite?
Part of this stems from recently getting up to speed with Python, which has Only One Way To Do It, which means, when you see It, you know exactly what It Is Doing. I've been reading a lot of other people's perl lately, some of it quite old and crufty, and I'm struck by the tendency of perl hackers to be clever. And that's nice, and all, but sometimes it's not. And oh, I know, the community is diverse and all, but it does my head in to go to "help" communities that emphasise clear and readable code, and then say "And you know that annoying 4 line foreach loop? You can replace it with a single line of incomprehensible punctuation using map, in case carriage return is expensive on your machine!".
Which said, I may be missing something. Should I be using map, and if so, why? What does it give me other than shorter code? Do you find it just as easy to parse as a nice foreach loop, or can you do things with it that you just couldn't do with foreach?
On a related note, I think this has something to do with my innate distrust of "$_". I know, I know, "$_" is a defining characteristic of the language, but still. It's *clever*, and I don't always like clever. So map not only generally has $_ scattered throughout, but by definition, when reading it, you get to $_ before you have *any* idea what actual set the map is working on.
This, I suppose, is why I no longer code for a living, but I wonder how much of it really does just have to do with perl.
Anyway.
I have never been a fan of perl's map function.
There. I've said it. It confuses me, and makes me feel stupid. Partly, of course, this is self-fulfilling - I don't like it, so I don't use it, so I don't understand it when I see it, so I fear it, so I don't like it ....
Working in Lisp a bit recently has, oddly, increased both my understanding *and* my dislike. So I'd like to be educated.
Dear perl hackers. Does map give me *anything* other than syntactic sugar, or perhaps the opposite?
Part of this stems from recently getting up to speed with Python, which has Only One Way To Do It, which means, when you see It, you know exactly what It Is Doing. I've been reading a lot of other people's perl lately, some of it quite old and crufty, and I'm struck by the tendency of perl hackers to be clever. And that's nice, and all, but sometimes it's not. And oh, I know, the community is diverse and all, but it does my head in to go to "help" communities that emphasise clear and readable code, and then say "And you know that annoying 4 line foreach loop? You can replace it with a single line of incomprehensible punctuation using map, in case carriage return is expensive on your machine!".
Which said, I may be missing something. Should I be using map, and if so, why? What does it give me other than shorter code? Do you find it just as easy to parse as a nice foreach loop, or can you do things with it that you just couldn't do with foreach?
On a related note, I think this has something to do with my innate distrust of "$_". I know, I know, "$_" is a defining characteristic of the language, but still. It's *clever*, and I don't always like clever. So map not only generally has $_ scattered throughout, but by definition, when reading it, you get to $_ before you have *any* idea what actual set the map is working on.
This, I suppose, is why I no longer code for a living, but I wonder how much of it really does just have to do with perl.
(no subject)
Date: 2010-11-10 02:09 pm (UTC)Perl has a zillion ways to do it, PBP gives you one, and good reasons why you should do that one.
As for map? There are good reasons why a functional idiom is neater than a procedural one, and if you follow the PBP rules, it's even quite readable.
map itself is relatively boring on its own, but in combination with the other "functions for list data": grep, join, reverse, and sort, it can be a fairly huge amount neater, more concise *and* more readable than the procedural equivalent.
ETA: You are right that perl hackers *do* have a tendency to be overly clever. Functional paradigm coding is not *necessarily* overly clever, but is often misused that way.
(no subject)
Date: 2010-11-10 02:12 pm (UTC)A more helpful way to think of map might be in the context of data pipes. It's common to see something like @newlist = map { ... } sort { ... } grep { ... } @oldlist. You can unfold the map and grep into a foreach, but not the sort, because it operates over the entire list, not each individual element.
Bug 3161 has a great example of what I would consider a "bad" (nonintuitive if not broken) foreach loop that I replaced with a map.
(no subject)
Date: 2010-11-10 02:38 pm (UTC)That's actually one of my mild annoyances with Python - I can't conveniently express a filter in-line, unless there's a built in boolean function already that does the job I want.
I can certainly define my own boolean method right there, but I don't want to have to define and name a single use method when the method body code is itself smaller than the method name and definition. It's a minor negative consequence of a lack of braces and syntactically significant whitespace. Probably outweighed by a number of the other positive factors of that choice, but still, annoying.
(no subject)
Date: 2010-11-10 02:46 pm (UTC)foreach
is more readable. I generally agree withmap
is something I normally can't grok. I normally have to look it up each time I encounter it.But, you know. I think there's kind of a pressure here to use
map
and such, since the rest of the code uses it.(and yes, the foreach works with lexically scoped variables too)
(no subject)
Date: 2010-11-10 03:05 pm (UTC)I have a rant brewing about "The perl mindset". I've been either using or avoiding perl for 10 years now, and branching out into other languages and communities has made me think that "the perl community" suffers from a certain inferiority complex. Because perl was very early in making the leap from "scripting language" to "full blown programming language", I think it had a lot of defensiveness. Python didn't have so much to prove, because perl had already done the "No, we're interpreted and scripting *and* heavy lifting". I think "the perl community" took a certain amount of pride in "clever" for that reason. So map gets used *because* it's clever and compact *and* tricky. I think the attitude is "Perl hackers must be so clever because we can overcome the hurdles our own language puts in our way! Anyone can code in a language that isn't actively trying to confuse them!". Anyone can write a foreach loop. Doing it with map must be better, because it's more confusing!
But I may be bitter....
(no subject)
Date: 2010-11-10 06:37 pm (UTC)I.e., you can just start speaking it, it will be horrendous, but it will work. Just don't expect to re-use it afterwards.
Writing really really good Perl is about as hard as writing really really good English. That is to say, *really fucking hard*.
This is why I keep banging on about Damian Conway's Perl Best Practices. It's the equivalent of the Oxford Dictionary plus a complete grammar and usage style guide like Fowler or Strunk&White. Then you have Perl::Tidy and Perl::Critic to implement those rules, which is the equivalent of your spellchecker and grammar checker *and* an editor looking over your shoulder at all times.
Perl Code written to comply with Conway's PBP is generally readable, no matter who wrote it and how damn "clever" or not they are trying to be. It may not be bug free, and the code may still raise the question of "why did you do that?", but you can always read the code and understand what it actually does.
(no subject)
Date: 2010-11-13 05:44 am (UTC)It strikes me that there's no sane linguist on the planet who would ever design a language like English. It's a bad starting place for a whole bunch of reasons (not least of which is the aforementioned difficulty speaking it goodly).
(no subject)
Date: 2010-11-13 06:37 am (UTC)As for designed languages, well, Esperanto worked out great! ;-)
(no subject)
Date: 2010-11-13 06:42 am (UTC)(no subject)
Date: 2010-11-13 07:09 am (UTC)I think it's the opposite, actually. Because English has been spread through colonial influence, it's become fuzzy and developed a low level entry case. Ie, English was shaped by it's spread, not spread due to its shape.
If you were developing a programming language, you could make the argument that this means its a good model due to having a decent amount of user testing. I wouldn't make a strong case that this makes sense for a programming language, though. Unless of course you happen to be a colonial power in a dominant position willing to force your code practices onto the nations you've dominated.
(no subject)
Date: 2010-11-13 08:38 am (UTC)As for whether it's a good idea for a programming language? For a "proper" programming language, gah, no it's terrible. OTOH for cobbling some stuff together that mostly works without having to think too hard that you don't expect to have a long lifetime? It's great.
(no subject)
Date: 2010-11-13 10:33 am (UTC)As for the times and places for different programming languages, well, I'll obviously defer to your thoughts there. Although their spread does have a lot the same properties - ie, they generally tend to move with colonisation rather than necessarily being "better" or more appropriate. You need to be good enough, and have a vector of propagation and colonisation.
(no subject)
Date: 2010-11-13 11:05 am (UTC)(no subject)
Date: 2010-11-13 11:11 am (UTC)(no subject)
Date: 2010-11-13 12:16 pm (UTC)I'm fairly sure the argument is that with many other empire-conquest languages, you either get creoles between the empire-lingo and local-lingo which then develop into their own "proper" language, or you actually wind up with both in common usage and they remain distinct... whereas with English it's been a little more common to wind up with heavy English usage by the conquered including loss of local language quite quickly. It's all messy though and all reverse reasoning... not anything anyone can really prove. :-) But yes, conquest/colonisation definitely plays a big part in the spread.
I do agree, many programming languages are definitely popular "just because everyone uses them" for a particular field, not necessarily because that one is better than the available alternatives. Enterprise Java, PHP Web "programming", unix shell scripting, Flash web games... Yech. :-) All have much better alternatives, but they've got a lot of entrenched usage and so they'll all be around a good long while.
(no subject)
Date: 2010-11-10 05:18 pm (UTC)(no subject)
Date: 2010-11-10 02:49 pm (UTC)I never use it in my code unless I'm forced to.
(for some of my other thoughts on this post, see my reply to
(no subject)
Date: 2010-11-11 02:23 am (UTC)For a 1 letter variable, that means two 78 character lines, maximum... so if I need $_ for more lines than that, it's probably bad, and I should do it some other way. :-)
(no subject)
Date: 2010-11-10 09:24 pm (UTC)result = [func(x) for x in stuff if test(x)]
Where func(x) and test(x) are statements. x is your $_ equivalent except you get to choose its name and stuff is your source iterable. Python 3.something gives you dictcomps which are the same thing over dictionary keys.
(no subject)
Date: 2010-11-10 09:35 pm (UTC)I think it's a personal thing. If it's a test, and then a func to do to things, then they're logically separate. So separate them. A large part of it may be confidence, too - if I have
for thing in stuff
do stuff to thing
then when it all goes horribly wrong, I can add "print thing" before the do stuff.
Or it occurs to me that I need to clean the stuff before I do the thing, so I can put "clean stuff" before "do thing".
And with a loop, I can just add lines. With any sort of one liner, I look at it and think "Um....". And kariela is right, I can just keep adding pipes, but that very quickly becomes a whole line of noise, and if I have to add one thing, I'll probably have to add other things. And then I have to print the things I got from the first stuff before I do the second stuff and oh god I hate it.
So I think it's actually language independent. If you're doing things to stuff, get the stuff, then do things to it, don't try to do it all at once!
And bringing it back to Lisp, from the little I know, you *do* break it up over lines, and you can just make it (otherstuff (stuff (things))) *or* (stuff (otherstuff(things))) at any point in there. But it probably sucks in lisp too.
(no subject)
Date: 2010-11-10 10:09 pm (UTC)strings = [str(x) for x in maybe_strings]
less_than_5 = [x for x in numbers if x < 5]
numbers_if_not_none = [int(x) for x in things if x is not None]
and the like. Anything that's more complicated than that generally gets done in a for loop.
(no subject)
Date: 2010-11-10 10:19 pm (UTC)my @newlist = map { "text-prefix" . $_ } qw(some array of plan text right there);
or, see if I get this right
newlist = [ ("text-prefix" + x) for x in ["some", "array", "of", "plain" "text"]]
So I can see the text, I can see it's only adding a text string, it's the Right Solution (tm). Because you are, literally, mapping a string to a new string. Not some poorly defined possible result of a method that may or may not return something to some other whacky transformation.
So it's not perl's fault. Poor misunderstood perl.
(no subject)
Date: 2010-11-11 02:11 am (UTC)(no subject)
Date: 2010-11-11 03:21 am (UTC)@less_than_5 = grep { $_ < 5 } @numbers;
The third example is more difficult if you *really* want a one-liner, since you're modifying the array as well:
@numbers_if_not_none = map { int($_) } grep { defined($_) } @things;
I wouldn't write that second one as a one-liner, myself. I would do:
my @numbers_if_not_none = ();
foreach my $number (@things) {
if (defined($number)) { push(@numbers_if_not_none, int($number)); }
}
Some other people might write the line in the loop as:
push(@numbers_if_not_none, int($number)) if defined($number);
...but I personally prefer putting the
if
first.(no subject)
Date: 2010-11-12 10:33 am (UTC)