Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Pronoun Programming

by rje (Deacon)
on Feb 04, 2008 at 19:03 UTC ( [id://666047]=perlmeditation: print w/replies, xml ) Need Help??

I've seen this discussed somewhere before, but I don't recall if it was in Perl's context or not.

I was writing some Perl just now -- a line with a statement modifier -- and wished it had the prounoun "it".

Here's the code as I wrote it:

delete $log_count{ $key } if $log_count{ $key } == 0;


and here's how I sort of wanted to write it:

delete $log_count{ $key } if it == 0;


Does that make sense to anybody else?

Replies are listed 'Best First'.
Re: Pronoun Programming
by BrowserUk (Patriarch) on Feb 04, 2008 at 23:38 UTC

    delete is a awkward choice for your example, because it is one of Perl's weird operators. Those that cannot be recreated using user code.

    In most (all other?) situations, $hash{ $key } refers to a simple scalar value. Either the value of that key, or undef.

    But for delete to operate, it needs to know a) the hash from which the element is to be deleted; b) the key of that element. In most languages that would necessitate passing the two identifiers (or references to them) separately: delete( \%hash, $key ); or calling a method on the hash and passing the key: %hash->delete( $key );.

    Perl only gets away with the syntax it uses because of the way it's parser acts directly on the source, which allows it to see the single argument $hash{ $key } as two separate entities, not the single resultant of the combined expression.

    I've often wished that Perl would allow me to take a reference to a hash element. The concept doesn't make much sense in Perl as implemented, but it would allow such constructs as: $$_ == 0 and delete $$_ for \$hash{ $key };

    And exists $$_ and $$_ = 'some value' for \$hash{ $key };, and a bunch of other situations where one finds oneself having to repeat the construct.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      You could of course tie your hash to return values that allow for something similar.

      You would have to choose something other than delete $val to keep it simple, but $val->delete is almost as good. Unfortunately, this falls down on assignment. I can't think of any good way to do it ($val->set($newval) is probably as good as it gets).

      Also, you'd have to do some nasty business about garbage collection. You wouldn't want to keep an entire hash around just because you still have one element of the hash.

        I think a hash element reference would have to be a pointer to an HE struct. That contains pointers to both the key and it's value. The thing that is missing is some way to get back to the hash it is a part of, as they form a single linked list going in the wrong direction.

        There are several ways I would like to extend Perl's hashes, had I the skills and patience. This is one. Another would be to add timestamps to keys, that gets set when keys are created. That would allow for time ordered hashes which could be useful. And the same field could be used to order keys according to any arbitrary sorting order, which would be more space and time efficient than current tied hash+array hybrid solutions.

        But, as almost every attempt I've made at messing with Perl's internals has resulted in random, inexplicable crashes which my attempts to resolve have fallen on deaf ears and stoney ground, it's not something I am likely to get around to any time soon.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Pronoun Programming
by Util (Priest) on Feb 05, 2008 at 00:17 UTC

    It makes sense to me!

    In Perl, 'it' is spelled '$_', and is an important part of the design of Perl 5, growing more important in Perl 6, and in the Perl 6 pieces that were back-ported into perl 5.10. Remember, Larry Wall is a linguist by training, and understands the value of pronouns. From Perl 6 and Parrot Essentials:

    $_ is always the current topic (think "topic of conversation"), so the process of aliasing a variable to $_ is known as topicalization.

    Consider this expression:

    $entirely{TOO}[$long]{$var} = 42 if $entirely{TOO}[$long]{$var} < 5;
    In Perl 5, the idiom for briefly topicalizing a variable is the single-item for statement:
    for ( $entirely{TOO}[$long]{$var} ) { $_ = 42 if $_ < 5; }
    This is slightly jarring at first; we expect a for loop to, well, loop! I had to use the idiom a few times to get used to it.

    If you need multiple (yet discrete and update-able) pronouns at the same time, the Data::Alias module can create them. ($his and $hers?)

    Unfortunately, none of this can help with your specific example. The delete function, like exists, does not actually operate on a hash value; it is more of a method call on the hash "object" with the key as a parameter. In fact, I believe that in Perl 6, delete %myhash{$mykey} is really just syntactic sugar for %myhash.delete($mykey).

    You can look at it this way: nearly everything you do with %myhash{$mykey} involves retrieving the value stored in the $mykey bucket, doing something with it, and possibly storing it back in its bucket. The major exceptions are delete and exists, which deal with destroying or verifying the bucket *itself*. The fact that we use the same %myhash{$mykey} syntax for everything is just for convenience; the cases are quite different under the hood. An alias that allows deletion of the alias-forming-key may be too much to expect, but I agree that it would be convenient.

      This is slightly jarring at first; we expect a for loop to, well, loop! I had to use the idiom a few times to get used to it.

      For the case that for(foo) is an alias for 'for the case that foo is true', though, it makes perfect sense, and a for loop with nothing to iterate over is such a case. :-)

Re: Pronoun Programming
by hossman (Prior) on Feb 04, 2008 at 22:19 UTC

    Eh ... the problem with pronouns in English, is that except for the occasional simplistic conversation, you can never be certain what a pronoun is referring to -- it is inherently ambiguous, and requires a lot of fuzzy logic inference based on context.

    For example: in the line i just wrote, what is "it" ? ... you as a person understood that "it" was a back reference to "a pronoun" (and not "English" or "simplistic conversation") but if you can write software smart enough to infer context like that -- there are a lot better uses for it then the perl compiler.

    Frankly: I don't want source code to read like English -- English is messy and confusing.

Re: Pronoun Programming
by Tanktalus (Canon) on Feb 04, 2008 at 20:30 UTC

    Agreed. Totally. But I can't imagine how to do that generically.

    some_func($a, $b, $c) if it > 3; # um, is that $a, $b, or $c > 3? All 3? (using "they" instead of "i +t")?

    Something tells me perl6 will have some sort of arrow operator to make this easier ;-)

Re: Pronoun Programming
by fergal (Chaplain) on Feb 04, 2008 at 23:49 UTC

    Something close to what you want exists but not in Perl. Before that though, there are problems with what you want. Consider

    delete $log_count{$a--} if it=0;
    should $a get decremented? Decremented twice? Only decremented when the condition is true?. The later is what you'd really want but is basically impossible because you have to undo side-effects. That's not so bad for decrement but what about
    delete $hash{some_destructive_function()} if it==0

    That said, what you're asking for is very close to something that already exists, called "anaphoric if". It can be achieved with macros in a language like lisp and it's implementation is described in the book "On Lisp" by Paul Graham. Even if you have no intention of every writing a line of Lisp, that book is well worth reading and available for free (it is not however an introduction to Lisp and you'll need to know a certain amount before it makes any sense).

    Let's imagine we had macros in Perl - real ones, like Lisp macros (or maybe Perl6, I haven't really looked), not the nasty hackish macros of the C preprocessors that are basically just text-mungers. Real macros allow us to pull code apart and join it back together again safely.

    I'm going to pretend that the syntax allows for code quotes - so

    $code = c{ $a = 1; $b=2; print $a + $b}
    assigns into $code a piece of parsed code. I'm also going to imagine that $$name gets replaced with the code contained in $name. So I can do something like
    sub printer { my $expr = shift; return c{ sub { my $x = shift; my $y = shift; print $$expr; } } }

    printer takes as an argument an expression and returns a subroutine that applies that expression to its 2 arguments and prints them out. So

    $adder_code = printer( c{ $x + $y } } $adder_sub = compile($adder_code) &{$adder_sub}(1, 2) # prints 3 $pythago_code = printer( c{ sqrt($x**2 + $y**2) } } $pythago_sub = compile($pythago_code) &{$pythago_sub}(3, 4) # prints 5

    So that's code quotes. For real macros, you need them to blend in, you don't want to have to insert code quotes all over the place when you're using them. So we'll say that if something is declared a macro it gets passed it's arguments as code, they do not get evaluated. Also we don't have to call compile, the resulting code gets compiled right there and replaces the macro (much like C macros but far more powerful because we can do lots more than just cutting and pasting text). So if we change the above to be

    macro printer { my $expr = shift; return c{ sub { my $x = shift; my $y = shift; print $$expr; } } }
    then we can drop the c{} stuff and just say
    # printer is a macro, so $x + $y is not evaluated $adder_sub = printer( $x + $y } &{$adder_sub}(1, 2) # prints 3

    So what's this got to do with anaphoric ifs? Well,

    macro aif { my $condition = shift; my $body = shift; return c{ { my $it = $$condition; if ($it) { $$body; } } } }
    and now I can write something like
    aif(number_of_widgets(), { print "there are $it widgets\n"; } )

    You could go on to implement what you are looking for originally too but it would have all the pitfalls of double evaluation of the expression.

    aif could actually be implemented with just C-style macros but the more interesting macros in On Lisp cannot.

    You can also define awhile and some other useful anaphorisms

    Finally, I suppose you could write something that examines the expression more closely and understands hashes and deletes and does exactly what you want and does it correctly but that's a lot of understanding to build in

      aif could actually be implemented with just C-style macros but the more interesting macros in On Lisp cannot.

      This implies that a source filter could be used here to create aif().


      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Pronoun Programming
by apl (Monsignor) on Feb 04, 2008 at 20:33 UTC
    I really like the idea, but think it would be a nightmare to process in the case of delete $log_count{ $key++ } if it == 0;
Re: Pronoun Programming
by eric256 (Parson) on Feb 04, 2008 at 21:47 UTC

    What about:

    for ($log_count{$key} ) { delete $_ if $_ == 0 }

    I wonder if you could make a sub it() that returns $_ so you could say delete it if it == 0; :)


    ___________
    Eric Hodges
      What about...

      What about it? Other than that it doesn't compile, I mean?

        Hehe very true. I was thinking of a regular scalar and just expanded it to the hash. It does seem kind of strange to me that delete $hash{$key}; works but $_ = $hash{$key}; delete $_ doesn't.


        ___________
        Eric Hodges
Re: Pronoun Programming
by Tux (Canon) on Feb 05, 2008 at 07:06 UTC

    Would it also work the other (my preferred) way round?

    $log_count{$key} == 0 and delete it; delete it if $log_count{$key} == 0;

    /me enters parsing hell ...


    Enjoy, Have FUN! H.Merijn
Re: Pronoun Programming
by hipowls (Curate) on Feb 05, 2008 at 11:05 UTC

    With perl 5.10 you can do this

    use 5.010_000; my %humbug = ( foo => 'bar', fu => 'bah', ); $_ = 'humbug'; say; foreach my $key ( keys %humbug ) { given ( $log{$key} ) { say if $_; } } say; __END__ humbug bah bar humbug
    It doesn't work for delete , see BrowserUK's explanation.

Re: Pronoun Programming
by ambrus (Abbot) on Feb 05, 2008 at 08:58 UTC

    I'd more like something like this for exists:

    if (exists($foo{$bar}) { push @quux, it; }
Re: Pronoun Programming
by Jenda (Abbot) on Feb 05, 2008 at 14:59 UTC
Re: Pronoun Programming
by diamond (Acolyte) on Feb 05, 2008 at 16:53 UTC
    Why not collect the candidates and delete them all at once:

    delete @log_count{grep($log_count{$_} == 0, keys %log_count)};

    A little compressed, I know. You could break it into two lines, I guess:

    @bad_log_entries = grep($log_count{$_} == 0, keys %log_count); delete @log_count{@bad_log_entries};

    Just a thought...

    Brett Diamond

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://666047]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2024-04-19 02:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found