Perl: Review my code



  • About two years ago, I was helping out someone at Perlmonks, and in a bout of frustration I decided to rewrite/refactor his script because it was a mess.

    Now, the popular opinion is that everything written in Perl looks like line noise. So I'd like feedback on what's wrong with the rewritten script. And which part of it looks like line noise. [1]

    As I said in the post back then, I know it's got a couple of flaws; mostly that many lines pass the 80-column mark, and the three functions being practically identical in logic -- only the strings change.

    Oh, and please disregard the toilet humour. It's not mine -- it's from the original script.

    
    #!/usr/bin/perl
    #
    # Flashy's bloodsports. http://digdilem.org/ - "Because thems trouts ain't gonna catch themselves!"
    # (Credit to the original Eggdrop TCL script by Nerfbendr)
    #
    # Adds silly !hunt, !fish and !trophy and !present public triggers
    # Also !newmonth will clean out the trophy cupboard. (Only if you do it, or the nick in $owner_nick)
    #
    # Optional Configuration (Will work fine without changing these, but you can if you like)
    my $owner_nick='rainbowwarrior'; # Your nick - you can only reset the scores remotely if you use this nick.
    my $scale = 'kg'; # Say here whether you want to measure weights in lb or kg.
    my $catch_percent=90; # How often you catch or shoot something.
    my $trophy_cabinet = Xchat::get_info( 'xchatdir' ) . "\\trophies.txt"; # File to keep the trophies in.
    # End user configuration
    
    use strict;
    use warnings;
    use Storable qw/nstore retrieve/;
    
    Xchat::register( "rainbowwarrrior's Huntin' 'n Fishin' and present unwrapping", 'v.004', "Hunting", "" );
    Xchat::hook_print('Channel Message', "hunting");
    Xchat::hook_print('Your Message', "hunting");
    Xchat::hook_print('Private Message', "hunting");
    Xchat::hook_print('Private Message to Dialog', "hunting");
    my %records;
    load_trophy();
    Xchat::print("\002Loaded Flash's Huntin' 'n Fishin'\002 (!hunt, !fish !present !trophy - Current records are $records{fish}{weight}$scale and $records{hunt}{weight}$scale)");
    
    
    my %items = (
    	hunt => ["bear","gopher","rabbit","hunter","deer","fox","duck","moose","pokemon named Pikachu","park ranger","Yogi Bear","Boo Boo Bear","dog named Benji","cow","raccoon","koala bear","camper","channel lamer"],
    	fish => ["Salmon","Herring","Yellowfin Tuna","Pink Salmon","Chub","Barbel","Perch","Northern Pike","Brown Trout","Arctic Char","Roach","Brayling","Bleak","Cat Fish","Sun Fish","Old Tire","Rusty Tin Can","Genie Lamp","Love Message In A Bottle","Old Log","Rubber Boot","Dead Body","Loch Ness Monster","Old Fishing Lure","Piece of the Titanic","Chunk of Atlantis","Squid","Whale","Dolphin","Porpoise","Stingray","Submarine","Seal","Seahorse","Jellyfish","Starfish","Electric Eel","Great White Shark","Scuba Diver","X","Lag Monster","Virus","Soggy Pack of Smokes","Pile of Weed","Boat Anchor","Pair Of Floaties","Mermaid","Merman","Halibut","Tiddler","Sock","Trout"],
    	present => ["a new car","cake","guitar","dvd","xbox360","playstation3","hairdryer","trampoline","dog","cat","electric shock game","a board game","a snake","tickets to a rock concert","a blowup doll","book"],
    );
    my %places = (
    	hunt => ["in some bushes","in a hunting blind","in a hole","up in a tree","in a hiding place","out in the open","in the middle of a field","downtown","on a street corner","at the local mall"],
    	fish => ["Stream","Lake","River","Pond","Ocean","Bathtub","Kiddie's Swimming Pool","Toilet","Pile of Vomit","Pool of Urine","Kitchen Sink","Bathroom Sink","Mud Puddle","Pail of Water","Bowl of Jell-O (tm)","Wash Basin","Rain Barrel","Aquarium","SnowBank","WaterFall","Cup of Coffee","Glass of Milk"],
    	present => ["in a cupboard","under a xmas tree","in the liverroom","up in a tree","in a hiding place","out in the open","upstairs","on a boat","on a street corner","at the local mall"],
    );
    my %last_users;
    @last_users{qw/hunt fish present/} = ("Nobody") x 3;
    
    sub reset_game {
    	%records = (
    		hunt => { qw/type bear weight 0 where Bush who Nobody/ },
    		fish => { qw/type Trout weight 0 where Pool who Nobody/ },
    		present => { qw/type xmas weight 0 where everywhere who Nobody/ },
    	);
    }
    
    sub random_place {
    	my ($type) = @_;
    	die "Wrong type of place" unless $places{$type};
    
    	my $ary = $places{$type};
    	return $ary->[int rand scalar @$ary];
    }
    
    sub random_item {
    	my ($type) = @_;
    	die "Wrong type of item" unless $items{$type};
    
    	my $ary = $items{$type};
    	return $ary->[int rand scalar @$ary];
    }
    
    sub random_weight {
    	my ($type) = @_;
    	die "Wrong type of record" unless $records{$type};
    
    	return int(rand($records{$type}{weight} + 10)) + 1;
    }
    
    sub hunt {
    	my ($hunter) = @_;
    
    	my $place = random_place('hunt');
    	my $game = random_item('game');
    
    	my @msgs = (
    		[PRIV => "You hide $place and wait for something to wander by...",
    		         ".", "..", "...",
    		         "You think you hear something and fire wildly in that direction!"]
    	);
    
    	if (rand(100) > $catch_percent) {
    		push @msgs, [PRIV => "Rats...you missed it, $hunter! Better luck next time!"];
    		push @msgs, [PUB => "$hunter is useless, they missed by a mile!"];
    		return @msgs;
    	}
    
    	my $weight = random_weight('hunt');
    
    	push @msgs, [PRIV => "Congratulations, $hunter! You just bagged yourself a $weight$scale $game!"];
    	push @msgs, [PUB => "$hunter just bagged a $weight$scale $game."];
    
    	if ($weight > $records{hunt}{weight}) {
    		push @msgs, [PRIV => "Wow!!! That's a new record! Way to go, $hunter! Type !trophy to see it!"];
    		push @msgs, [PUB => "Wow! That breaks the old record of a $records{hunt}{weight}$scale $records{hunt}{type}! $hunter is amazing!"];
    
    		$records{hunt} = {
    			weight => $weight, type => $game,
    			who => $hunter, where => $place };
    		save_trophy();
    	}
    
    	return @msgs;
    }
    
    sub fish {
    	my ($hunter) = @_;
    	my $place = random_place('fish');
    	my $fish = random_item('fish');
    
    	my @msgs = (
    		[PRIV => "You cast your line into a $place and wait for a bite...",
    		         ".", "..", "...",
    		         "You feel a tug on your line and reel it in..."]
    	);
    
    	if (rand(100) > $catch_percent) {
    		push @msgs, [PRIV => "Rats...it got away, $hunter! Better luck next time!"];
    		push @msgs, [PUB => "$hunter is useless, they failed to catch anything!"];
    		return @msgs;
    	}
    
    	my $weight = random_weight('fish');
    	push @msgs, [PRIV => "Congratulations, $hunter! You just caught yourself a $weight$scale $fish!"];
    	push @msgs, [PUB => "$hunter just caught a $weight$scale $fish"];
    
    	if ($weight > $records{fish}{weight}) {
    		push @msgs, [PRIV => "Wow!!! That's a new record! Way to go, $hunter! Type !trophy to see it!"];
    		push @msgs, [PUB => "Brilliant! That breaks the old record of a $records{fish}{weight}$scale $records{fish}{type}! $hunter is the world's best!"];
    
    		$records{fish} = {
    			weight => $weight, type => $fish,
    			who => $hunter, where => $place };
    		save_trophy();
    	}
    
    	return @msgs;
    }
    
    sub trophy {
    	my %verbs = (
    		fish => [fishing => 'caught'],
    		hunt => [hunting => 'bagged'],
    		present => ['present unwrapping' => 'unwrapped']
    	);
    
    	return map { [ 'PUB', $_ ] }
    		map { sprintf "%s holds the %s record when they %s a %s$scale %s", @$_ }
    		map { [ @$_[0, 3, 4, 1, 2] ] }
    		map { [ @{ $records{$_} }{qw/who weight type/}, @{ $verbs{$_} } ] }
    			qw/fish hunt present/;
    }
    
    sub present {
    	my ($hunter) = @_;
    	my $place = random_place('present');
    	my $present = random_item('present');
    
    	my @msgs = (
    		[PRIV => "You unwrap your present from $place and see what you get...",
    		         ".", "..", "...", "You unwrap and see ..."]
    	);
    
    	if (rand(100) > $catch_percent) {
    		push @msgs, [PRIV => "Sorry no present this time $hunter! Better luck next time!"];
    		push @msgs, [PUB => "$hunter is useless, they failed to get a present!"];
    		return @msgs;
    	}
    	
    	my $weight = random_weight('present');
    	push @msgs, [PRIV => "Congratulations, $hunter! You just unwrapped a $weight$scale $present!"];
    	push @msgs, [PUB => "$hunter just got a $weight$scale $present"];
    
    	if ($weight > $records{present}{weight}) {
    		push @msgs, [PRIV => "Wow!!! That's a new record! Way to go, $hunter! Type !trophy to see it!"];
    		push @msgs, [PUB => "Brilliant! That breaks the old record of a $records{present}{weight}$scale $records{present}{type}! $hunter is the world's best!"];
    
    		$records{present} = {
    			weight => $weight, type => $present,
    			who => $hunter, where => $place };
    		save_trophy();
    	}
    
    	return @msgs;
    }
    
    sub new_month {
    	my ($hunter, $mynick) = @_;
    	$hunter = lc $hunter;
    
    	if (not ($hunter eq lc $owner_nick or $hunter eq $mynick)) {
    		return [PUB => "Who are you, $hunter to tell me to change the month?"];
    	}
    
    	reset_game();
    	save_trophy();
    	return [PUB => "It's a new month, all existing huntin' 'n fishin' and present unwrapping records are reset!"];
    }
    
    sub check_hogging {
    	my ($type, $hunter) = @_;
    
    	if ($last_users{$type} eq $hunter) {
    		return [PUB => "Stop hogging all the best pitches $hunter, let someone else try first!"];
    	}
    
    	return;
    }
    
    sub load_trophy {
    	reset_game();
    	retrieve(\%records, $trophy_cabinet) or do {
    		save_trophy();
    		return;
    	};
    }
    
    sub save_trophy {
    	nstore(\%records, $trophy_cabinet)
    		or die "Bah! Can't open the trophy cabinet to push this 'ere trophy in!";
    }
    
    my %triggers = (
    	hunt     => \&hunt,
    	fish     => \&fish,
    	trophy   => \&trophy,
    	present  => \&present,
    	newmonth => \&new_month,
    );
    
    sub hunting {
    	my ($who, $msg) = @{ $_[0] };
    	my @pubwords = split(/ /, $msg);
    
    	my $trigger = substr( lc $pubwords[0], 1 );
    	my $func = $triggers{ $trigger };
    
    	return unless $func;
    
    	my $hunter = Xchat::strip_code($who);
    	my $channel = Xchat::get_info('channel');
    	my @msgs = check_hogging($trigger, $hunter);
    
    	if (!@msgs) {
    		@msgs = $func->($hunter, Xchat::get_info('nick'));
    	}
    
    	for my $msg (@msgs) {
    		my $type = shift @$msg;
    		if ($type eq 'PUB') {
    			Xchat::command("msg $channel $_") for @$msg;
    		} elsif ($type eq 'PRIV') {
    			Xchat::command("msg $hunter $_") for @$msg;
    		} else {
    			Xchat::print("Unknown message type: $type");
    		}
    	}
    }
    

    Please follow the perlmonks link if discourse's overflowing div is too small for you. Or copypaste it to a real syntax-highlighted editor.

    [1]: (Sorry, I think this topic came about because of the jab the front-page article took at Perl, again. It's a good language with a consistent design, if you ask me. It's just not the popular opinion.)

    p.s. I'm not sure what's the copyright status on that script; it's someone else's script but the code is 95 % mine. Of course, I can't exactly prove that since I posted it anonymously back then, but...


  • BINNED

    @hhaamu said:

    Now, the popular opinion is that everything written in Perl looks like line noise. So I'd like feedback on what's wrong with the rewritten script. And which part of it looks like line noise. [1]

    I think you're taking the popular criticism of forth much too seriously. It's really just one of those things people who have no plans to use a language they don't like say about it. A similar complaint about Lisp is that it's unreadable because of parentheses and macros.

    Maybe this image will help:



  • Yeah, I'm quite calm and generally ignoring them but I just can't help noticing that every time the language is mentioned, there's always someone blindly jeering. It's gotten old.

    I know I've got at least one WTF in the code: sub trophy {} is too clever for my own sake.

    I'm fine even if you point and laugh at the code, as long as it's got some merit. So go ahead.



  • Perl will always hold an special place in my heart, but, it has so many symbols (&,$,%,etc) that it makes my head hurt.

    OTOH, it would have been such a much better choice than JS for the browser. Stupid Netscape.



  • Impressions from someone who doesn't do perl...

    • Code is sort of understandable. Large number of strings help.

    • There are ruby-like unless conditionals:

      die "Wrong type of place" unless $places{$type};
      

      I never liked these, but it's interesting perl has them.

    • Functions can't take named arguments?

      sub random_item {
      	my ($type) = @_;
      }
      

      That fucking sucks.

    • Functions and code are just strewn around the source file. Not much structure. Combined with previous point, it really reminds me more of a bash script than actual language.

    • Is this a global var?

      sub reset_game {
      %records = (
      	hunt => { qw/type bear weight 0 where Bush who Nobody/ },
      	fish => { qw/type Trout weight 0 where Pool who Nobody/ },
      	present => { qw/type xmas weight 0 where everywhere who Nobody/ },
      );
      }
      

    Overall, this looks OK - for a script. I don't see the facilities I would need to make a real project, which some people are advocating.


  • I survived the hour long Uno hand

    Here's why I hate perl. This code doesn't work right, mostly because I wrote it for Apples to Apples (which never quite worked) and have tried to retrofit it to play Cards Against Humanity because their licensing terms are better

    Lines that have never made much sense to me:

    if (defined($nick)) {
    	$self->{_nick} = $nick;	
    }
    

    What do the braces do in that line?

    if (defined(@hand)) {
    	$self->{_hand} = @hand;	
    }
    

    Why is that line scalar context?

    while (exists($game->{_dealt}{$rand})) {
    		$rand = int(rand($Apples::nouncount));
    }
    

    Braces AND arrows now!

    if (defined($self->{_players}{$who})) {
    	delete($self->{_players}{$who});
    	$self->Num_players($self->Num_players-1);
    	Logios::IRC_print($where, $who . " is no longer playing.");
    }
    

    Approaching line noise rapidly, and I wrote the damn thing

    foreach $player (sort {$self->{_players}{$b}->{_score} <=> $self->{_players}{$a}->{_score} } (keys %{$self->{_players}})) {
    	Logios::IRC_print($where,"   " . $player . ": " .$self->{_players}{$player}->{_score});
    }
    

    I suspect my friend wrote that line for me.


  • I survived the hour long Uno hand

    @cartman82 said:

    Functions can't take named arguments?

    Nope. Fixed in perl 6.



  • @hhaamu said:

    mostly that many lines pass the 80-column mark,

    That guideline is obsolete. Don't worry about it.

    Modern computers have these things called "bitmapped displays", one of the most amazing things about them is they can visual split long lines into two lines, even if the original source file only contains one. It's like a miracle. (Then again, I wager anybody writing Perl is using an ancient 1980s-era text editor.)

    @hhaamu said:

    and the three functions being practically identical in logic -- only the strings change.

    That's less desirable, depending on how "practically identical" they actually are.

    As far as the actual code goes, it looks perfectly readable to me (someone who's barely had any experience with Perl.) I noticed you almost (entirely?) avoided using RegEx, which is the thing that makes most Perl scripts into unreadable messes.



  • @cartman82 said:

    - Functions can't take named arguments?

    ```
    sub random_item {
    	my ($type) = @_;
    }
    ```</blockquote>
    

    $type is sort of a named argument. You see, Perl function calls take a list. The list is unpacked into named arguments, typically in the beginning of the function. It's often quite helpful if you ask me, but I'm sure it doesn't please the 'typed variables' crowd.

    - Functions and code are just strewn around the source file. Not much structure. Combined with previous point, it really reminds me more of a bash script than actual language.

    Oh, that's a common fault of mine. I tend to group them as 'sub-functions' that are directly above the functions that need them.

    - Is this a global var?
    ```
    sub reset_game {
    %records = (</blockquote>
    

    It is. Defined at the top of the file.


  • I survived the hour long Uno hand

    @hhaamu said:

    $type is sort of a named argument

    No, it's not, it's assigned via position. Named arguments are like https://msdn.microsoft.com/en-us/library/dd264739.aspx

    If you do not remember the order of the parameters but you do know their names, you can send the arguments in either order, weight first or height first.
    CalculateBMI(weight: 123, height: 64);
    CalculateBMI(height: 64, weight: 123);



  • @blakeyrat said:

    mostly that many lines pass the 80-column mark,

    That guideline is obsolete. Don't worry about it.

    Well, I find longer line lengths difficult to read. That's really the only reason I try to adhere to it.

    @blakeyrat said:

    I noticed you almost (entirely?) avoided using RegEx, which is the thing that makes most Perl scripts into unreadable messes.

    There's one. In the split(/ /, $msg)

    Not sure you can call it a real regexp though, when it's only a single character in length.

    Anyway, not much text parsing in there so no need for regexps.


  • kills Dumbledore

    @blakeyrat said:

    avoided using RegEx, which is the thing that makes most Perl scripts into unreadable messes.

    Why is it that Perl is associated with regex stew? Accident of history? Somehow suited to the kind of task that needs lots of regex?



  • @hhaamu said:

    Well, I find longer line lengths difficult to read. That's really the only reason I try to adhere to it.

    Fine; but the correct place to worry about that is on the display layer. (i.e. fix yo' IDE. Or start using an IDE. Then fix it.) The length of the line in the source code is irrelevant.

    @Jaloopa said:

    Why is it that Perl is associated with regex stew?

    Have you ever looked at the standard Perl project? It's a well-deserved association, IMO.

    @Jaloopa said:

    Somehow suited to the kind of task that needs lots of regex?

    RegEx is one of those things we as a community of professionals should declare "a bad idea" and shove off into the corner, forgotten.


  • kills Dumbledore

    @blakeyrat said:

    Have you ever looked at the standard Perl project?

    No, pretty much avoided any exposure to the language

    @blakeyrat said:

    RegEx is one of those things we as a community of professionals should declare "a bad idea" and shove off into the corner, forgotten

    QFT



  • @Jaloopa said:

    No, pretty much avoided any exposure to the language

    I was going to link you to Slashcode's (probably one of the largest, well-known Perl projects in existence) repo, but the link's broken. Hahaha.


  • kills Dumbledore

    Probably a broken regex



  • @blakeyrat said:

    RegEx is one of those things we as a community of professionals should declare "a bad idea" and shove off into the corner, forgotten.

    QFT! Why, oh, why, do languages not ship with a proper set of parser combinators instead? Infinitely more versatile, and much more readable than regex soup, too!



  • @Yamikuronue said:

    Lines that have never made much sense to me:

    if (defined($nick)) {
    	$self-&gt;{_nick} = $nick;	
    }
    

    What do the braces do in that line?

    $self is a hashref, as it most often is?

    if (defined(@hand)) { $self->{_hand} = @hand; }

    Why is that line scalar context?

    I haven't the faintest clue; I'd possibly treat that line as an error. It doesn't look right to me.

    while (exists($game->{_dealt}{$rand})) { $rand = int(rand($Apples::nouncount)); }

    Braces AND arrows now!

    Hashref. A nested one.

    [snip code] Approaching line noise rapidly, and I wrote the damn thing

    ...yeah.

    foreach $player (sort {$self->{_players}{$b}->{_score} <=> $self->{_players}{$a}->{_score} } (keys %{$self->{_players}})) { Logios::IRC_print($where," " . $player . ": " .$self->{_players}{$player}->{_score}); }

    I suspect my friend wrote that line for me.

    Use a temp variable and ... it's still not readable but a tad better. Custom sorts can't be made pretty, if you ask me.

    my $plrs = $self->{_players};
    
    for $player (
        sort { $plrs->{$b}->{_score} <=> $plrs->{$a}->{_score} } keys %$plrs ) {
       Logios:: ... $plrs->{$player}{_score};
    }

  • I survived the hour long Uno hand

    @hhaamu said:

    Custom sorts can't be made pretty

    Compared to a custom sort I wrote in Java:

    @Override
     public int compareTo(Product p) {
            return displayOrder.compareTo(p.displayOrder);
     }
    

    Because OOP is a lot easier to read for this type of use case >.>



  • @Yamikuronue said:

    No, it's not, it's assigned via position. Named arguments are like https://msdn.microsoft.com/en-us/library/dd264739.aspx

    So like in Objective-C?

    Perl does have named arguments then, in a way, if you parse the args list as a hash. The bad part is that you can't call the function 'normally' then, but you have to always name the arguments.

    sub calculate_bmi {
    	my (%args) = @_;
    	return $args{weight} / (($args{height} / 100) ** 2)
    }
    
    # ...are we calculating this for a veeery obese baby?
    calculate_bmi(weight => 123, height => 64);


  • @tarunik said:

    QFT! Why, oh, why, do languages not ship with a proper set of parser combinators instead? Infinitely more versatile, and much more readable than regex soup, too!

    I use regex pretty often. IMO the readability problem isn't due to regular expressions themselves, it's that people for some reason forget to document them the way they would any other complex piece of code. Long, hard-to-read expressions should be broken up into smaller parts with comments explaining what each part does, then concatenated together.


  • BINNED

    @hhaamu said:

    every time the language is mentioned, there's always someone blindly jeering

    Blind jeering is a common phenomenon in the industry. People who have never tried Lisp say it's unreadable because of the parens. People who have never tried Forth say it's just plain unreadable (they may have a point). People who have never tried Haskell say you have to have a PhD in Math to use it.

    To answer your original question, let me just state for the record that I'm a card-carrying member of the Perl-haters club. I took a brief look at the language about a decade ago and ran away screaming when I found out how Perl implemented objects. That said, I can read your code just fine.


  • BINNED

    That's what all the "unreadable mess" insanity is about? Lucky bastards I say! Proper control structures? Decent functions with actual return statements? Only one global? You can NOT use globals?

    exten => s,1,Verbose(2, SUB : subDoesSomething)
        same => n,Set(_ATTEMPTS=0)
        same => n,Set(_ATTEMPTS=${MATH(${ATTEMPTS} + 1,i)})
        same => n,Gotoif($[ ${ATTEMPTS} > 4]?hangup)
        same => n,Verbose(${ATTEMPTS})
        same => n(reEnter),Playback(/var/asterisk-messages/message6)
        same => n,Playback(/var/lib/asterisk/sounds/en/beep)
        same => n,GoSub(subInput,s,1(8))
        same => n,Set(mjmj=${extension})
        same => n,Playback(/var/asterisk-messages/message7)
        same => n,Playback(/var/lib/asterisk/sounds/en/beep)
        same => n,GoSub(subInput,s,1(8))
        same => n,Set(custid=${extension})
        same => n,Verbose(2, MJMJ = ${mjmj} and custid = ${custid})
        same => n,Set(devicetype=${ODBC_ENTRYTYPE(${mjmj},${custid})})
        same => n,GoSubif($[ "${devicetype}" = "1"]?entryRegularDevice)
        same => n,GoSubif($[ "${devicetype}" = "K"]?entryCombinedDevice)
        same => n,GoSubif($[ "${devicetype}" = "T"]?entryDeviceState)
        same => n,Playback(/var/asterisk-messages/message8)
        same => n,Goto(s,reEnter)
        same => n(entryRegularDevice),GoSub(subEntryRegularDevice,s,1)
        same => n,Goto(exit)
        same => n(entryCombinedDevice),GoSub(subEntryCombinedDevice,s,1)
        same => n,Goto(izlaz)
        same => n(entryDeviceState),GoSub(subEntryDeviceState,s,1)
        same => n,Goto(exit)
        same => n(exit),Return
        same => n(hangup),Hangup() 
    
    

    And here, have a sub. Have fun with those arguments!

    [subMixMonitor]
    exten => s,1,MixMonitor(${ARG1}-${ARG2}-${REPLACE(CDR(start),\ ,-)}.wav,,b)
    

    DISCLAIMERS:

    • Poorly anonymized / translated variable names, sorry. The original ones are... decent-ish
    • Not all my code, can't remember which parts are who's though
    • GoTo variations are not a WTF. Well, in a sense that there is no other choice. That's all the damned thing knows how to do.


  • @antiquarian said:

    Blind jeering is a common phenomenon in the industry. People who have never tried Lisp say it's unreadable because of the parens. People who have never tried Forth say it's just plain unreadable (they may have a point). People who have never tried Haskell say you have to have a PhD in Math to use it.

    You know, I haven't seen any lisp code longer than a three-line snippet, so I don't really know how deep the parentheses go there, but I'm not entirely unconvinced they don't have a point. (Did I just make three negations?) I didn't have problems matching parens back when BASIC was the only language I had (age ten?), and I even enjoyed using handfuls of them, but nowadays, it's just a pain and I have to insert whitespace strategically to make any series of parens and paren-like symbols readable.

    There's half a dozen of languages I'd like a working knowledge in, but since every language seems to take 2-3 years to reach fluency -- if using them exclusively --, I don't think I have enough time in my life for them. Haskell, incidentally, not being one of them.


  • I survived the hour long Uno hand

    @hhaamu said:

    but since every language seems to take 2-3 years to reach fluency

    The more languages you know, the shorter that gets.



  • It (lisp) can be quite bad, but any decent editor does match-parens and fairly ok indentations today, so it is only a problem when reading code outside the editor and with f***ed up linebreaks...


  • BINNED

    @hhaamu said:

    You know, I haven't seen any lisp code longer than a three-line snippet, so I don't really know how deep the parentheses go there, but I'm not entirely unconvinced they don't have a point. (Did I just make three negations?) I didn't have problems matching parens back when BASIC was the only language I had (age ten?), and I even enjoyed using handfuls of them, but nowadays, it's just a pain and I have to insert whitespace strategically to make any series of parens and paren-like symbols readable.

    People who use Lisp on a regular basis say that the parens don't really cause any problems. The key thing is you have to use an editor that's better than notepad. Most of them use emacs, which has a mode that will autocomplete the parens for you, and also has automatic indentation.

    More on the parens-hate:

    The reason many people who have learned C prefer to continue on the C branch of evolution is that they found the process of learning all that syntax quite painful. This is especially true for parentheses in C. You only need them in expressions that cross a fairly high complexity threshold and you can get rid of them by simplifying the expressions. Lisp is chock full of parentheses, with no way to get rid of them -- if you simplify your expressions, you end up with more parentheses. The "syntax = pain" equation in most C programmer's heads translates to a desire to reduce the cost of learning a new language by trying to adapt it to the pain they have already been through.



  • What about the fact that RegEx in the most common uses is PLAIN WRONG!? For example, all of those email address "validators" or URL "validators". Things that (for all practical purposes) can not be validated correctly in RegEx.

    It's a great way to write bad code to get bad results, though.


  • ♿ (Parody)

    @blakeyrat said:

    What about the fact that RegEx in the most common uses is PLAIN WRONG!? For example, all of those email address "validators" or URL "validators".

    Are those really "most common uses," or just, "most commonly wrong uses?"

    I've used regexes for lots of things, but never either of those.


  • I survived the hour long Uno hand

    Stay tuned, there's an article in the queue about that very topic. ;)



  • @blakeyrat said:

    What about the fact that RegEx in the most common uses is PLAIN WRONG!? For example, all of those email address "validators" or URL "validators". Things that (for all practical purposes) can not be validated correctly in RegEx.

    Using the wrong tool for a job doesn't mean the tool should be thrown out. If it did we would be out of tools.



  • It's fine. Personally, I would use some nice OO wrapper libraries, to give you some nicer OO features like named arguments and the like. And (syntactically) split up those long lists with some well placed newlines.

    Personally, I don't like the style where you say

    if (condition) { push $msg @msgs }
    

    That makes it really hard to know what's in the list without mentally simulating the entire computation. Simulation is bad.

    Good work chaining map.

    Based on what I see here I can recommend that you are ready to try out Haskell.



  • Perl -- annoying and a slog to read, but I can slog through it if I have to
    Lisp (well, Clojure in my case) -- I don't find the parens to be much of a problem, but you do wind up altering your indenting style to help with this -- syntax highlighting is A Good Idea™ in any case, though
    Forth -- Dabbled in it extremely briefly -- not enough to be familiar with the readability issues though
    Haskell -- Same as Forth

    @antiquarian said:

    People who use Lisp on a regular basis say that the parens don't really cause any problems. The key thing is you have to use an editor that's better than notepad. Most of them use emacs, which has a mode that will autocomplete the parens for you, and also has automatic indentation.

    Yep -- if you think of Lisp parens more like C braces than C parens, you're already in better shape.

    @blakeyrat said:

    What about the fact that RegEx in the most common uses is PLAIN WRONG!? For example, all of those email address "validators" or URL "validators". Things that (for all practical purposes) can not be validated correctly in RegEx.

    It's a great way to write bad code to get bad results, though.


    Yeah -- some folks just need to be hit over the head with a cluebat and then handed a set of parser combinators to use.


  • BINNED

    @tarunik said:

    Yeah -- some folks just need to be hit over the head with a cluebat and then handed a set of parser combinators to use.

    I'm not sure they would know what to do with parser combinators (unless you have a really good cluebat).



  • @cartman82 said:

    There are ruby-like unless conditionals:

    die "Wrong type of place" unless $places{$type};

    I never liked these, but it's interesting perl has them.

    See if you can guess where those Ruby-like conditionals came from....

    Hint: they came from Perl! Ruby is basically a head-on collision between Perl and Smalltalk.



  • @Jaloopa said:

    Why is it that Perl is associated with regex stew? Accident of history? Somehow suited to the kind of task that needs lots of regex?

    Perl has built in operators for doing regular expressions on things. Yes, operators and not just functions.

    For instance, I can do a RegEx search like so:

    if ($variable =~ /bacon/i)

    Yes, I'm aware that I didn't use any regex operators there, but that will still do a case-insensitive search of $variable and return true if it finds it. You can also do substitution the same way, but with s/stuff/more stuff/

    For that matter, PCRE (Perl-Compatible Regular Expressions) seems to be one of the more popular implementations of RegEx. They're the ones you'll use in PHP, for example.

    Side note: It's been years since I did anything with Perl... a short stint in 2007 being the only time I touched it professionally. And you don't want to see what my older Perl code looked like.



  • The reason Perl exists is because Larry Wall liked individual aspects of Awk, Bash, C and Sed, and decided that it'd be fun to try and combine them into a single language. And I suppose you could argue that he succeeded—he made as good a stab at it as anyone could.

    I'm going to put forward the "language design as experiment" hypothesis:

    • Perl (see above)
    • C++ (can Bjarne shoehorn Simula into C?)
    • Objective-C (can Brad force the Smalltalk object model into C?)
    • Scheme (let's rip Lisp down to the bare syntax and rebuild it from scratch, only sane this time)


  • @hhaamu said:

    >if (defined(@hand)) { $self->{_hand} = @hand; }
    Why is that line scalar context?

    I haven't the faintest clue; I'd possibly treat that line as an error. It doesn't look right to me.


    $self->{_hand} is a scalar. You can make it a reference to @hand, but it's still a scalar. Since the author didn't do anything to make it a reference, it just provides a scalar context to @hand.

    @hhaamu said:

    Custom sorts can't be made pretty, if you ask me.
    Best thing I know if is to hide the ugliness by stuffing it into a function that (hopefully) you never have to look at again.



  • @hhaamu said:

    Perl does have named arguments then, in a way, if you parse the args list as a hash. The bad part is that you can't call the function 'normally' then, but you have to always name the arguments.

    There are library functions (Getopt::Long::GetOptions() comes to mind) that will accept a hashref as the first argument. If the first argument is a hashref, it treats it as a list of named args. If not, it assigns the args positionally, as normal.



  • Well, this function could indeed use some more love.

    sub hand {
        my $self = shift;
        # bug
        my @hand = shift;
    
        # deprecated, emits a warning
        if (defined(@hand)) {
            # bug
            $self->{_hand} = @hand;
        }
        # possible bug
        return $self->{_hand};
    }
    

    I think you were confused on whether you wanted to deal with array references or actual arrays. I quite liked Ruby for a while since everything was a reference there, so there was no chance of confusion, until Perl's semantics finally clicked. I still like Ruby, but for other reasons. (Rails is not one of these reasons.)

    If you wanted arrays, this is how the function should look:

    sub hand {
        my $self = shift;
        my @hand = @_;
    
        if (@hand) {
            $self->{_hand} = \@hand;
        }
        return @{ $self->{_hand} };
    }
    

    If arrayrefs,

    sub hand {
        my $self = shift;
        my $hand = shift;
    
        if ($hand) {
            $self->{_hand} = $hand;
        }
        return $self->{_hand};
    }
    

    It rather makes sense from Larry's linguist viewpoint. Singular subject comes with singular object. Plural comes with plural. If you mix $ (singular) and @ (plural), it looks like a grammatical error / bug.


  • Discourse touched me in a no-no place

    @Jaloopa said:

    Why is it that Perl is associated with regex stew? Accident of history? Somehow suited to the kind of task that needs lots of regex?

    IIRC it's the latter. Perl came about, again, IIRC, as a report writer, more or less, specifically to combine sed/awk/grep into one thing. Text manipulation is its bread and butter.


  • Discourse touched me in a no-no place

    @Yamikuronue said:

    Lines that have never made much sense to me:

    if (defined($nick)) {
    	$self->{_nick} = $nick;	
    }

    What do the braces do in that line?

    They're surrounding a key to a hash. I'm not sure if you could have got away with $self->_nick there; the rules for Perl barewords were never something I read in great depth.

    Why is it called _nick in the first place, and not just nick? What good does the leading _ do? (I admit I've not read the rest of the code. 😄)



  • It's a Perl custom to name private instance variables and methods with a leading underscore.[1] Although I'm not really sure about the instance variables part; most people don't go touching the privates of an object. I think it's just a poorly named instance variable -- it shouldn't need that underscore.

    ("Perl doesn't have an infatuation with enforced privacy. It would prefer that you stayed out of its living room because you weren't invited, not because it has a shotgun." -- perlmodlib)

    It's a bit like with LaTeX, where module privates are marked with the @ character.


    [1] I mean, in the vanilla OO Perl offers. The OO modules available may implement real access restrictions.



  • @dkf said:

    I'm not sure if you could have got away with $self->_nick there; the rules for Perl barewords were never something I read in great depth.

    No, it considers that an attempt to call a method, with the error

    Can't call method "_nick" on unblessed reference

    unless $self is blessed, of course, in which case it either calls the method or complains that it doesn't exist. Actually, in that case, it's on the LHS of an assignment, so it will also complain about that if the method does exist, so even if the _nick method existed, it wouldn't be valid there.



  • Ooh, do one for Go!



  • And, if you have time, you can also do one for the language I keep telling everyone about.



  • @ben_lubar said:

    Ooh, do one for Go!

    Alright then:

    • Go (did any of DMR's magic rub off on Ken1, can we finally bury Objective-C and salt the ground where it lies?)

    1. This is a little unfair - Ken Thompson invented B in 1969 which was, unsurprisingly, a direct influence on C.



  • @ben_lubar said:

    also do one for the language I keep telling everyone about.

    ❓
    kulet alak bidok nicol anam gatal mabdug zustash sedil ustos emär izeg bemòng gost öntak tosid feb berim ibruk ermis thoth thatthil gòstang libash lakish asdos roder nel biban ugog ish robek olmul nokzam emuth fer uvel dolush agêk ucat ngárak enir ugath lisig etäg erong osed lanlar udir tarmid sákrith nural bugsud okag nazush nashon âtrid enôr dùstik kogan ingish dudgoth stalkòb themor murak alåth osod thîkut cog selsten egdoth othsin idek ùst suthmam ím okab onlìl gasol tegir namàsh noval shalig shin lek äkim kâkdal stumäm alud olom ëlot rozsed thos okon nïng ostar rorul kovath åblel stal girtol kitïg lokast reked comníth sidos setnek ethbesh nug mokez cös idos ogîk utheg tílgil ebsas lurak tobul ilush dënush rimtar kun äs kiror nicat onshen rërith mafol sid ìtdùn tilat îtat egot dib oril bukog atot imik sudir odshith rag dodók sinsot es dostob gast élmeth romlam avéd darål ân oddom zägel og shatag om gisëk balad nekik dakas dolek sog rafar laltur îkeng dan ozsit dunan uling dîbesh berath zangin shadkik innok vukcas metul than gesul ustir torish memrut usal ôm angrir cagith momuz zas deshlir astesh ôfid mothram rít nolthag matul irtir unul urist umom dëm lodel kodor alod nökor asen råsh ursas vakun thol kizbiz uthgúr ônor gar terstum zagith noshtath ub ùk amur minran idar rodnul nuggad okbod tun måmgoz fak ogon rëmrit stidest zag kosak sub shegum addor talin zin åmmeb sakub tig edir tath vesh etest atír lorsïth rir em deb shuthraz èshgor rùkal acöb okir arngish zilir im ïssun...



  • !(programming_language)
    http://en.wikipedia.org/wiki/Go
    (programming_language)



  • @tar said:

    kulet alak bidok nicol anam gatal mabdug zustash sedil ustos emär izeg bemòng gost öntak tosid feb berim ibruk ermis thoth thatthil gòstang libash lakish asdos roder nel biban ugog ish robek olmul nokzam emuth fer uvel dolush agêk ucat ngárak enir ugath lisig etäg erong osed lanlar udir tarmid sákrith nural bugsud okag nazush nashon âtrid enôr dùstik kogan ingish dudgoth stalkòb themor murak alåth osod thîkut cog selsten egdoth othsin idek ùst suthmam ím okab onlìl gasol tegir namàsh noval shalig shin lek äkim kâkdal stumäm alud olom ëlot rozsed thos okon nïng ostar rorul kovath åblel stal girtol kitïg lokast reked comníth sidos setnek ethbesh nug mokez cös idos ogîk utheg tílgil ebsas lurak tobul ilush dënush rimtar kun äs kiror nicat onshen rërith mafol sid ìtdùn tilat îtat egot dib oril bukog atot imik sudir odshith rag dodók sinsot es dostob gast élmeth romlam avéd darål ân oddom zägel og shatag om gisëk balad nekik dakas dolek sog rafar laltur îkeng dan ozsit dunan uling dîbesh berath zangin shadkik innok vukcas metul than gesul ustir torish memrut usal ôm angrir cagith momuz zas deshlir astesh ôfid mothram rít nolthag matul irtir unul urist umom dëm lodel kodor alod nökor asen råsh ursas vakun thol kizbiz uthgúr ônor gar terstum zagith noshtath ub ùk amur minran idar rodnul nuggad okbod tun måmgoz fak ogon rëmrit stidest zag kosak sub shegum addor talin zin åmmeb sakub tig edir tath vesh etest atír lorsïth rir em deb shuthraz èshgor rùkal acöb okir arngish zilir im ïssun...

    If anyone's wondering, the English translation of that is:

    abbey ace act after age ageless ale ancient angel anger animal ape apple arch arm armor arrow artifice ash aunt aura autumn awe-inspiring axe back bad bald bake ball bane bar barb bath battle beak beast bear bear_verb bee beer beetle beguiler belch berry big bile bin bird black blade blaze blind blister bloat blood bloody blossom blue boar boat bodice bog boil boil_v bold bolt bone book boot bother bow bow_verb brain breach breeches bread break breakfast breath breed brew bride bridle bright bristle brilliant brim broil brother brunch buck buckle bunch burden burn bury bush bushel bust_noun bust_verb business busy butcher butter butterfly button buzzard cackle cad cage cake call camp cancer candle candy canker canyon carnage casket castle cat cave cell chain chant chaos chamber char charm child chill chip_noun chip_verb chirp choke chop chuck cinder clam clap clasp claw clear clearing cleave cling cloak clobber clock clod cloister closet club cluster clutter cobra coil cold color comet conqueror cook cover cotton couple crab crawl craze crazy cream creek creep creepy crescent cross_noun cross_verb cross_adj crow crown crucify crumble crush crusher cry crypt crystal cuddle cudgel cup curse cut cyst dance dangle dank dagger dark date_fruit date_verb dawn day dead dear death decay deceiver deep deer demon despair destroyer destruction devil devourer dine dinner dirt ditch dog domestic donkey doom door dragon drain drawl dreg dress_clothing dress_general drinker drip drool drowned drum dry dump dumpling dung dungeon dusk dust duty dye eagle ear east eat ecto eel eerie egg elder entrails eternal ever everlasting...


Log in to reply