Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Half-serious quest for prefix anonymous refs taking

by blazar (Canon)
on May 29, 2008 at 16:25 UTC ( [id://689047]=perlmeditation: print w/replies, xml ) Need Help??

You often want to make a list into an anonymous arrayref. So you just do: [EXPR], where EXPR is the expression returning the list. But sometimes it can be quite complex: while I feel perfectly comfortable with chaining several functions in very long statements, as long as I indent adequatly of course, I would feel guilty putting a lonely open square paren at one pont and a lonely closing one say, three lines later.

For example in the last example of a recent post of mine I had two subs returning a list, and then compared them by taking two anonymous arrayrefs. (Via Test::More's is_deeply.) But if for any reason I wanted them to return arrayrefs directly, I would rewrite them as:

sub dargv { local *ARGV; @ARGV = @_; [<>]; } sub dopen { my @t = map { open my $fh, '<', $_ or warn "Can't open `$_': $!\n"; <$fh>; } @_; \@t; }

respectively. Not so bad, certainly. And somebody would argue that the latter is clearer because it's "more explicit." Yet it somehow bothers me to have to assign to a temporary variable only to take a reference to it, and sometimes I would like a prefix anonymous reference maker.

Of course, I may just roll my own helper sub:

sub mkaref { [@_] }

Thinking about, I may write similar subs for other kinds of refs:

# the most reasonable thing for this strange beast, IMHO sub mksref { @_ == 1 ? do { my $x=$_[0]; \$x } : "@_"; # users can play with (a localized) $" } sub mkhref { my %x; @x{@_}=(); \%x; } sub mkcref { my @x = @_; sub { @x }; # a closure }

But then this feels all so clumsy, and I'd like an idiomatic, buitin way to do the same. Granted: I'm not claiming that we really need such a thing, but anyway I'd like to know -for fun- which syntax you would think could be appropriate for such beasts.

Personally, since the referencing operator \ clearly speaks "reference" and the sigils unambiguously identify the kind of reference, I would use \$, \a, \% and \&, followed by... something... except that any symbol I can think of, even if separated by whitespace, would make for some special variable. So, end of story. But I'd like to hear your thoughts and ideas. (Except if you only want to say that we don't really need this, because I know!)

--
If you can't understand the incipit, then please check the IPB Campaign.

Replies are listed 'Best First'.
Re: Half-serious quest for prefix anonymous refs taking
by kyle (Abbot) on May 29, 2008 at 16:49 UTC

    I see a different problem with dopen

    my $x = dopen( 'file-not-found' ); __END__ Can't open `file-not-found': No such file or directory readline() on closed filehandle $fh

    Ouch. How about this instead:

    my $x = dopen( '/dev/null', 'file-not-found', '/etc/passwd' ); sub dopen { my @t = map { my $fh; open $fh, '<', $_ and <$fh> or warn "Can't open `$_': $!\n"; } @_; \@t; } __END__ Can't open `/dev/null': Can't open `file-not-found': No such file or directory

    That has a different problem. It reports an error just because the file happens to be empty.

    How about this, then:

    my $x = dopen( '/dev/null', 'file-not-found', '/etc/passwd' ); sub dopen { my $out = []; FILE: foreach my $file ( @_ ) { my $fh; if ( ! open $fh, '<', $file ) { warn "Can't read '$file': $!\n"; next FILE; } push @{$out}, <$fh>; } return $out; } __END__ Can't read 'file-not-found': No such file or directory

    Looks good! It's readable, it complains at the right times, and it doesn't die when I meant it to warn.

    I know this does not answer the question you asked. You seem to be looking for a way to do "sub dopen { [ map { open my $f, '<', $_ or warn "Can't open `$_': $!\n"; <$f> } @_ ] }" without the oh-so-confusing [] hanging around at the outskirts. My point is, that's the least of your problems. Write it so it works and so that someone can understand it.

      I see a different problem with dopen

      I personally believe you're perfectly right. But then this definitely belongs to the other thread.

      Letting this aside, any way you look at it, these solutions take far too much code to be really in accordance with Perl's motto that "easy things should be easy." Of course, it's very perlish to use modules instead, and that's a valid alternative. But one feels that this is such a basic thing that there must must be a cheap idiom for it. One more reason for wanting some more magic in local(@ARGV), in connection with an argv pragma.

      You seem to be looking for a way to do "sub dopen { [ map { open my $f, '<', $_ or warn "Can't open `$_': $!\n"; <$f> } @_ ] }" without the oh-so-confusing [] hanging around at the outskirts. My point is, that's the least of your problems.

      It's not my problem (in 99% of cases) and I hope not to have implied it. I still claim that it's a very minor inconvenience that can be source for thought. You seem to fail to understand that there are not only questions about how to get things done, but that some people like to think about programming language concepts and features and about those of their preferred one.

      --
      If you can't understand the incipit, then please check the IPB Campaign.
Re: Half-serious quest for prefix anonymous refs taking
by starbolin (Hermit) on May 29, 2008 at 18:29 UTC

    blazar writes:

    Yet it somehow bothers me to have to assign to a temporary variable only to take a reference to it,
    Except that a holder for the data is also created which will persist until the reference you created is destroyed. So it's not really 'temporary' and when viewed in that light the syntax does not appear so arbitrary.

    For quick and dirty code I'd write your dopen like this:

    sub dopen { my $ref = []; @$ref = map { open my $fh, '<', $_ or warn "Can't open `$_': $!\n"; <$fh>; } @_; return $ref; }
    Which communicates the intent of passing a reference. It's also more in line with the syntax of an object constructor should you need to rewrite using objects.

    It seems like what you are really trying to do is to re-invent perl objects outside of the symbol table. Which I'm not convinced would be such a good idea.


    s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}
      Except that a holder for the data is also created which will persist until the reference you created is destroyed. So it's not really 'temporary' and when viewed in that light the syntax does not appear so arbitrary.

      I personally believe that you're right. But the fact that the original variable won't be accessible from outside of the sub makes it "temporary" enough for me. In some cases this is the only way to go: you surely know that there's not an anonymous scalar ref constructor, and that people do do {my $x; \$x} instead: ain't it the same thing?

      It seems like what you are really trying to do is to re-invent perl objects outside of the symbol table.

      I don't have the slightest idea of why you think so. This is far from what I'm trying to do. Actually, I'm not trying "to do" anything: I'm rather reasoning about syntax and semantics. But in some deep sense I cannot grasp, you may be right: can you expand on the subject? In particular, can you explain which part of what I wrote may possibly have to do with the symbol table?!? (It seems to me that I'm only munging lexical variables...)

      --
      If you can't understand the incipit, then please check the IPB Campaign.

        The allusion to perl objects came more from the resemblance your dopen subroutine has to an object constructor plus my miscomprehension that you wanted to assign to an anonymous array. After rereading your post, I realize all you asking for was a indirect-object syntax for list constructors equivalent to the indirect-object syntax for list operators such as print and join. I still see similarities to perl objects, not in the Class and Package sense, but in that perl objects take arguments, possibly in indirect-object syntax, and return a reference. Only, in perl objects, the reference is blessed into a class which allows it to be used as a symbolic reference. Thus the symbol table reference.

        Yes, if perl allows indirect-object syntax for list operators then it could allow the same for list constructors. However the syntax would have the same ambiguities. Who hasn't typed something like print ( $x - 1 ) / 2; and had to puzzle out why the parser dropped the last term? In the OP you mentioned nested anonymous assignments but the proposed syntax would not allow nested assignments due to the ambiguous syntax.

        s/indirect-object/imperative/


        s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}
Re: Half-serious quest for prefix anonymous refs taking
by dragonchild (Archbishop) on May 29, 2008 at 17:22 UTC
    This starts to tickle one of my sore points with Perl. In DBM::Deep, there is a lot of code that says something like:
    my $r = Scalar::Util::reftype( $thing ); my $tied; if ( $r eq 'ARRAY' ) { $tied = tied @$r; } elsif ( $r eq 'HASH' ) { $tied = tied %$r; } else { die "How did we get '$r'??\n"; }
    There's a number of those problems, but arrays and hashes being so different sucks for me. Frankly, I don't care (nor want to care) about how they're implemented. If I want to ask, I'll ask and each should be able to answer is_hash() and is_array(). But, I shouldn't be forced to deference like that. It sucks.

    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: Half-serious quest for prefix anonymous refs taking
by ysth (Canon) on May 30, 2008 at 05:54 UTC

      I personally believe it's frightening! ++

      But seriously: I know my quest was claimed to be half-serious to start with, however it seems nobody let their fantasy fly and suggest some damned syntax for these beasts... C'mon guys! ;)

      --
      If you can't understand the incipit, then please check the IPB Campaign.
Re: Half-serious quest for prefix anonymous refs taking
by TGI (Parson) on May 30, 2008 at 00:45 UTC

    This looks like a job for prototypes. No, really :).

    use strict; use warnings; use Data::Dumper; sub mkcref (@) { my @closed = @_; return sub { @closed}; } sub mkaref (@) { return [@_]; } sub mkhref (%) { if ( @_%2 ) { warn "Odd number of elements in mkhref"; return { @_, undef }; } else { return {@_}; } } my $foo = mkaref 1,2,3,qw(a b c); print Dumper $foo; my $bar = mkhref a => 1, b => 2, 3; print Dumper $bar;

    I left it off mksref, because I wasn't sure how it should behave. It's an odd one.


    TGI says moo

      I left it off mksref, because I wasn't sure how it should behave. It's an odd one.

      I personally believe it should either complain if anything but exactly one argument is passed to it, or try to do something smart in that case, as I tried to, in my example. Another possibility would have been for it to return \[@_] but that would leave with two levels of indirection which is not consistent with the one argument case; further, it would amount to mksref mkaref LIST, so it doesn't make much sense.

      Another possibility that springs to mind is the following: if there were an anonymous (circumfix) scalar ref constructor, it would impose scalar context to its arguments. Thus it should behave as follows:

      sub mksref { my $x = +(@_)[-1]; \$x }
      This looks like a job for prototypes. No, really :).

      I can't see how prototypes could help there:

      sub mkcref (@) { my @closed = @_; return sub { @closed}; } sub mkaref (@) { return [@_]; }

      The @ prototype does not do anything useful, since it just says that the arguments are an arbitrary list, which is the default anyway.

      sub mkhref (%) { if ( @_%2 ) { warn "Odd number of elements in mkhref"; return { @_, undef }; } else { return {@_}; } }

      There's not a % prototype that I know of. There's a \% one which does something entirely different. If there were I believe it should simply do the evenness check you're doing yourself.

      Said this, my own version of mkhref() did something different: took a list and returned a hashref with the elements of that list as keys, and undef values. Given that these subs are supposed to be prefix alternatives to the circumfix [ ], { } and sub { } constructors, and to the non existant scalar ref one, my sub is certainly stupid and yours does the Right Thing™. But in the same vein, I would leave to perl to do its job of checking (if warnings are enabled) evennes:

      sub mkhref { {@_} }
      --
      If you can't understand the incipit, then please check the IPB Campaign.

        Update: As blazar so kindly pointed out, the (@) prototype is indeed meaningless. It just goes to show how useful it is to reread the docs every once in a while. I'd filed away the fact that prototypes let you do cool stuff and omit parens. Which they do, but not in the way that I understood it at the time I first read the docs.

        Reread the docs from time to time. It's amazing what you can learn!

        Back to my regularly scheduled foolishness...


        The % and @ prototypes do the same thing--eat an entire list. Your suggestion that it should check for evenness is very good. It's in my battered old copy of Perl in a Nutshell, it works, but I don't see it in perlsub--interesting...

        I used the % prototype despite its identicalness with @ just to clearly show my intentions--that is mkhref wants a hash. I'm not sure about that decision now, since it turns out to be a partially documented feature :).

        The prototypes allow you to skip your parentheses, which makes the sub act more like a built-in. Since you said you wanted a built-in, I thought I'd fake it as well as can be done.

        mkaref( 1..9 ); # becomes mkaref 1..9;

        In a module, I'd have mkhref check for warnings, with $^W. If on, it should carp, that way the error refers to the "right" line of code.

        sub mkhref (%) { if ( @_%2 ) { carp "Odd number of elements in mkhref" if $^W; return { @_, undef }; } else { return {@_}; } }


        TGI says moo

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (11)
As of 2024-04-18 14:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found