Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
Filburt Shellbach
Nov 6, 2007

Apni tackat say tujay aaj mitta juu gaa!
Your time is worth more than the computer's. Design the program well; don't worry about function or method call overhead until it actually becomes a problem (and it hopefully never will).

The term I've heard for what Triple Tech is describing is decoupling.

Adbot
ADBOT LOVES YOU

Mario Incandenza
Aug 24, 2000

Tell me, small fry, have you ever heard of the golden Triumph Forks?
Also, I'm not sure you need to use Tie::Handle::CSV when this is probably a lot faster:
code:
use Text::CSV_XS;

open my $csv_fh, '<', 'blah.csv';
my $csv = Text::CSV_XS->new;
$csv->column_names($csv->getline($csv_fh));

while (my $hashref = $csv->getline_hr) {
}

Beardless Woman
May 5, 2004

M for Mysterious
Having silly issues with map and can't seem to get it to work the way I want. I'm sure there's a way but it's escaping me.

I've got this code
code:
return map { $_->servdef => $_->description } $self->load_all;
Works just fine. But I only want to return the hash that matches $_->enabled.

So I try
code:
return map { $_->servdef => $_->description if ($_->enabled) } $self->load_all;
And that not only doesn't work, it destroys the structure of my hash.

I can get it to work right using

code:
   foreach ($self->load_all) {
      if ($_->enabled) {
         $available_addons{$_->servdef} = $_->description;
      }
   }

   return %available_addons;
But I'd much rather use map for the one-linerness of it all. Is this possible with map?

EDIT:

Turns out this works. Why it doesn't work without an else statement, I have no idea.
code:
map { if ( $_->enabled ) { $_->servdef => $_->description } else {} } $self->load_all;

Beardless Woman fucked around with this message at 04:02 on Dec 17, 2008

Filburt Shellbach
Nov 6, 2007

Apni tackat say tujay aaj mitta juu gaa!
Here's code that'll work:

code:
return map { $_->servdef => $_->description } grep { $_->enabled } $self->load_all;
The reason your code failing is because when a service is disabled, the map block returns (I think) the empty string instead of the empty list. So you get stuff like:

code:
'font server' => 'Serves fonts!', '' => '', '' => 'image server', 'Serves images!' => undef
Not that I recommend it, having the grep separate from the map is clearer, but you can also do:

code:
return map { $_->enabled ? ($_->servdef => $_->description) : () } $self->load_all;
This is occasionally useful in other contexts, such as when building up a hash where some elements are optional.

Beardless Woman
May 5, 2004

M for Mysterious

Sartak posted:

working code

Thanks Sartak. You're right about returning the empty string. I wound up just using a foreach loop. I shouldn't try to get too clever.

EDIT: After playing around with it, turns out you simply have to have an else inside map if you're using if.

Beardless Woman fucked around with this message at 12:12 on Dec 17, 2008

TiMBuS
Sep 25, 2007

LOL WUT?

Beardless Woman posted:

Why it doesn't work without an else statement, I have no idea.
Because perl returns the last evaluated statement or expression in a block
So without the else, it's returning the result of the if statement which i guess is a blank string. With the else there you're returning the result of a blank block which seems to be equivalent to undef.

Div
May 27, 2001

I'm hoping someone can help me here. I've been so wrapped up in this problem that I don't think I'm capable of thinking outside the box at all.

I'm writing a framework and part of that framework is an ORM.

The spec for the ORM is basically this:
1) Has to handle joins which span multiple database (a MySQL 'feature').
2) Models must be available across multiple projects and should always use the existing DB connection for that 'request' if applicable.
3) Provide CRUD functionality.

So I've written a loose port of the django model system in Perl.

The models look like this:

code:
package Project::Models::Feature;
use Project::Model::ORM;

extends 'Project::Model::Table';

table 'dm.features';

field 'feature_id'  => (type => 'Int',  primary_key => 1);
field 'title'       => (type => 'Char', length      => 255, required => 1);
field 'slug'        => (type => 'Slug', from        => 'title');
field 'posted'      => (type => 'Datetime', auto_on_add => 1);
field 'description' => (type => 'Text', required    => 1);
field 'status'      => (type => 'Enum', choices => ['open', 'development', 'stage', 'live']);

field 'reporter_id' => ( type => 'ForeignKey', relates_to => 'Project::Models::User', on => 'user_id',  rel_name => 'reporter' );
field 'assigned_to' => ( type => 'ForeignKey', relates_to => 'Project::Models::User',  on => 'user_id', rel_name => 'developer');

# essentials
__PACKAGE__->meta->make_immutable;
1;
Sample usage:

code:
use aliased 'Project::Models::Feature' => 'Feature';

my $db = Project::Database->new($host, $username, $password);
my $feature = Feature->new(db => $db);
$feature->title('"Test" Feature!');
$feature->description('A test feature');
$feature->reporter_id(5);
$feature->assigned_to(2);
$feature->insert();

print $feature->feature_id; # prints mysql_insertid;
print $feature->slug;       # prints 'test-feature'

$feature->title('Still testing');
$feature->update(reporter_id => 4); # updates title and reporter_id

# handles relationships too:

$feature = Feature->new(db => $db, id => 1);
print $feature->reporter->display_name; 
On the whole this system has went down well. It provides basic CRUD, field validation and accepts a db instance in its constructor so that the models can be used across projects. We use Apache::DBI but projects will generally have different username/passwords even if they can all access the same databases so Apache::DBI will see that as a different connection and open a new one. Unfortunately I have no control over this, so I need to work within this boundary.

Passing in the db instance to the constructor was fine when coming up with a proof-of-concept app for my framework with two controllers and a handful of actions. But what really started to bother me when writing the test suite was that I constantly had to pass in the db object to the constructor. Every model instance I was going to create in this test run was going to use the same settings, and the same could apply to every controller and action inside a given project - they would all be using the same settings. So in the spirit of DRY I wanted to add a new rule to the spec:

4) When used inside their native project, a model should fall back on a default connection defined by that project's config file.

This has proved a lot harder than I thought.

After multiple attempts, the solution that I've implemented right now is this (Moose syntax):

code:

# Project::Model::Class:

has 'db' =>
(
    is => 'rw',
    isa => 'Project::Database',
    lazy => 1,
    default => \&_buildDefaultDb
);

sub _buildDefaultDb
{
    my $namespace = ref $self;
    $namespace =~ s!::Model::.*$!::Config!;
    eval "use $namespace";
    my $config = eval "\$${namespace}::getInstance()";
    return Project::Database->new(config => $config);    
}

There are a couple of problems with this - one is the fact that each model creates its own instance of Project::Database which seems like a waste (especially considering the object instantation time hit of Moose). At the same time, because we use Apache::DBI opening and closing multiple DBI connections at the application level isn't a concern.

The other reason is that I plain don't like the technique I'm using here. It relies a lot on conventions and in general seems like a nasty hack. Also it doesn't really play well with a test suite. I end up having to create a directory structure idential to my project structure just to test the models.. it's so much hassle that I end up just passing in the db instance.

I have thought about requiring the creation of a "base model class" for each project and the doing something like:

code:
package Project::Model::Base;
use Moose;

extends 'Project::Model::Table';

__PACKAGE__->default_connection('host','username', 'password');
Then having each model extending that class, but I don't have to create a base class in django, so if possible I'd like to avoid doing it here.

What I'd like to know is if there are any ways of achieving what I want to - essentially giving a group of classes a default property that they can inherit. When I write it like that, creating a base class seems like the most logical option. But I'd be interested in hearing other suggestions...

Div fucked around with this message at 12:52 on Dec 18, 2008

Filburt Shellbach
Nov 6, 2007

Apni tackat say tujay aaj mitta juu gaa!

TiMBuS posted:

the result of the if statement which i guess is a blank string

The canonical false value in Perl is the empty string. For example, 1 > 3 evaluates to the empty string. The canonical true value is 1.

Div posted:

What I'd like to know is if there are any ways of achieving what I want to - essentially giving a group of classes a default property that they can inherit. When I write it like that, creating a base class seems like the most logical option. But I'd be interested in hearing other suggestions...

I'd look at Class::Data::Inheritable. It has some quirks, but I think they are quirks you want.

There Will Be Penalty
May 18, 2002

Makes a great pet!
Not a short question, but Perl was released 21 years ago today.

Yay Perl!

s139252
Jan 1, 1970
test

Sartak posted:

The canonical false value in Perl is the empty string. For example, 1 > 3 evaluates to the empty string. The canonical true value is 1.

(:siren: someone correct me if any of this is wrong -- going by memory)

It depends on context. The way false is represented in the Perl API is &PL_sv_no, which points at a table entry that contains context-sensitive read-only scalars to be used in op evaluation.

pre:
$r = (1 < 0) + 0;
In this case, false is integer zero, not empty string. The comparison evaluates to &PL_sv_no, and the addition operator sees that on the stack in numeric context, so it interprets it as an SvIV (an integer scalar) that has integer 0 as its value. In string context, &PL_sv_no does evaluate to an SvPV (a char* scalar) containing a pointer to an empty string. I think there is another context for non-integer math (an SvNV).

Similarly, there is &PL_sv_yes with scalars for "1", integer 1, or double 1 and also &PL_sv_undef for the undefined value, which is a scalar flagged as null.

TiMBuS posted:

Because perl returns the last evaluated statement or expression in a block
So without the else, it's returning the result of the if statement which i guess is a blank string.

The last statement of the block chosen by evaluating the expression is returned. The if construct itself has no "result" (hence the '?:' operator). A perl sub just returns the values off its stack (and Perl's return statement just puts values on the stack, which is what makes it optional).

pre:
sub a {
  if(1) { 0 }
  else { 1 }
}

a();  # numeric 0, not ''
You can assume this always returns 0 because that will always be the result of the last expression evaluated before the end of the sub.

s139252 fucked around with this message at 19:21 on Dec 19, 2008

magimix
Dec 31, 2003

MY FAT WAIFU!!! :love:
She's fetish efficient :3:

Nap Ghost
As it was explained to me once, the set of things that are 'false' consists of the empty string, numeric zero, and UNDEF. Conversely, the set of things that are 'true' is everything not in the set 'false'.

Filburt Shellbach
Nov 6, 2007

Apni tackat say tujay aaj mitta juu gaa!

magimix posted:

As it was explained to me once, the set of things that are 'false' consists of the empty string, numeric zero, and UNDEF. Conversely, the set of things that are 'true' is everything not in the set 'false'.

Yes. We're discussing which false value Perl chooses in certain situations.

magimix
Dec 31, 2003

MY FAT WAIFU!!! :love:
She's fetish efficient :3:

Nap Ghost

Sartak posted:

Yes. We're discussing which false value Perl chooses in certain situations.

Even in these formal shorts, I feel like a failure :(

TiMBuS
Sep 25, 2007

LOL WUT?

satest4 posted:

The last statement of the block chosen by evaluating the expression is returned. The if construct itself has no "result" (hence the '?:' operator).

Not so sure bout this. It has some kind of result, check it out:

$a = do{}; # $a is undefined
$a = do{if (1 > 2){} else{}}; # $a is also undefined here
$a = do{if (1 > 2){}} # $a is defined, but false.


and heres the kicker
undef $a; $a = do{if ($a){}}; # $a is undefined.

s139252
Jan 1, 1970
test

TiMBuS posted:

Not so sure bout this. It has some kind of result, check it out:

$a = do{}; # $a is undefined
$a = do{if (1 > 2){} else{}}; # $a is also undefined here
$a = do{if (1 > 2){}} # $a is defined, but false.


and heres the kicker
undef $a; $a = do{if ($a){}}; # $a is undefined.

Everything is stack-based and most ops will affect the stack.

$a = do{}; # $a is undefined
$a = do{if (1 > 2){} else{}}; # $a is also undefined here


Perl has an op called stub that pushes PL_sv_undef onto the stack (in scalar context). This is where the assignment gets undef from here. It is sort of a noop placeholder.

$a = do{if (1 > 2){}}

In this case, perl chooses to optimize away the evaluation of "1 > 2" during lexing and replaces it with a const op that pushes PL_sv_no onto the stack. The assignment operator provides scalar context, and above I explained PL_sv_no in a scalar context is an empty string.

The optimizer actually adds a bit of non-determinism here. Interesting find. :)

undef $a; $a = do{if ($a){}}; # $a is undefined.

This can't be optimized like the last example because the statement being evaluated is not a constant expression. As with the first examples, the stub op is used when the condition is true, giving you undef again.

TiMBuS
Sep 25, 2007

LOL WUT?

So if statements do have a result, it's just purely coincidental because of the stack-based implementation? Hey as long as it works.

But:

satest4 posted:

undef $a; $a = do{if ($a){}}; # $a is undefined.

This can't be optimized like the last example because the statement being evaluated is not a constant expression. As with the first examples, the stub op is used when the condition is true, giving you undef again.
Think you got confused here, $a is undefined and therefore won't run the stub.

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
Someone from #cobol told me the problem I'm trying to solve at work is called "dependency injection", and it seems to be right on the money.

Which would be the least evil way of accessing a hash our'ed by a package? eval or a symbolic reference?

I'm trying to implement what I would call data inheritance... Like the data is structured similarly in all the subclasses, but I don't want to expend the energy (hurf) in creating accessor stubs in each subclass, since they're exactly the same.

Edit: I realize this is a bit all over the place...

Filburt Shellbach
Nov 6, 2007

Apni tackat say tujay aaj mitta juu gaa!
Bread::Board might be worth looking at for inversion-of-control.

Class::Data::Inheritable for your data inheritance.

ashgromnies
Jun 19, 2004

Triple Tech posted:

Someone from #cobol told me the problem I'm trying to solve at work is called "dependency injection", and it seems to be right on the money.

Which would be the least evil way of accessing a hash our'ed by a package? eval or a symbolic reference?

I'm trying to implement what I would call data inheritance... Like the data is structured similarly in all the subclasses, but I don't want to expend the energy (hurf) in creating accessor stubs in each subclass, since they're exactly the same.

Edit: I realize this is a bit all over the place...

Are you sure you're going with the right solution? What are you trying to do this for?

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?

ashgromnies posted:

Are you sure you're going with the right solution? What are you trying to do this for?

The whole point of most of my CoC posts (related to this project) is that I have no idea if I'm using the right design, and that's what I'm seeking out.

Let's say we have a money making process, invoked by asking a goon (identified by user id) to make money.

1) Setup.
2) ???
3) Profit!

Steps 1 and 3 are the same for every goon, implemented the exact same way. Step 2 however is entirely dependent on which goon you ask. How do you implement such a pattern? (dependency injection??) To some extent, there's object inheritance, which I'm sort of using. But inheriting a method and passing around an anonymous subroutine are the same thing.

With this other process I'm making (different from the first process, but exactly the same in structure, 1, 2, profit), the "interface" if you will looks almost like a hash (value by key and collect all keys (used for testing)). So I figured, why not "inherit" a real hash. I accomplished this by just assembling the fully qualified (with package) name of the variable (this is a symbolic ref that breaks strict).

Yeah, it's all over the place and I'm not proud of it. But yeah, I don't know how else to go about it.

Mithaldu
Sep 25, 2007

Let's cuddle. :3:
Working on something related to the previous question i had and stumbled upon this: Is there a way to to somehow "use" a bunch of modules with only one command? Preferrably while having said command being a subroutine imported from another file.

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
use is a compile-time (clarify, someone, please) command, so the only way to really bundle it is the inclusion of another module. Just have a stuff called My::Bundle which in itself use's the other stuff.

You can require at run-time, however, so you can tuck those in a subroutine.

Mithaldu
Sep 25, 2007

Let's cuddle. :3:
So, simply like this?
code:
package MyModule;
use MyModule::RequiredModules;
---------------------------------------------------------
package MyModule::RequiredModules;

use A;
use B;
etc.

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
Right.

Mithaldu
Sep 25, 2007

Let's cuddle. :3:
drat, was hoping i did something wrong. When i do the stuff above, subs don't end up usable in MyModule when exported by "A" as follows:
code:
package A;
use base 'Exporter';
our @EXPORT = qw( lint_html extend_template_include_paths );
On the other hand, using "use base" allowed me to move all CGIApp plugins into a wrapper module, which is nifty.

s139252
Jan 1, 1970
test

Mithaldu posted:

drat, was hoping i did something wrong. When i do the stuff above, subs don't end up usable in MyModule when exported by "A" as follows:
code:
package A;
use base 'Exporter';
our @EXPORT = qw( lint_html extend_template_include_paths );
On the other hand, using "use base" allowed me to move all CGIApp plugins into a wrapper module, which is nifty.

Say you have this...

code:
# in Foo.pm
package Foo;
use AandB;

# in AandB.pm
package AandB;
use A;
use B;
Assuming "A" and "B" export subs with something like Exporter, they will export into the calling package ("AandB"). When you use "AandB" from "Foo", nothing is exported into "Foo" because "AandB" does not define any exports.

With "use base", you are modifying the caller's @ISA. That's probably less desirable for what you are trying to do, because (aside from other reasons) the inherited subs are visible via method resolution versus symbol table entries in the client package.

You could do something like this...

code:
package AandB;
use Exporter ();

sub import {
    # list modules and subs to import to caller
    my @MODULES = (
      [ 'A', qw/sub1 :all/ ],
      [ 'B', qw/foo bar/ ],
    );

    local $Exporter::ExportLevel = 1;
    foreach my $m (@MODULES) {
      Exporter::import(@{$m});
    }
}

Which would export specific symbols from 'A' and 'B' into a package that used 'AandB' (in theory... didn't test it).

Triple Tech posted:

use is a compile-time (clarify, someone, please) command

Right. Saying "use Foo" is the same as this...

code:
BEGIN {
  require Foo;
  Foo->import();
}
Since BEGIN blocks are eval'd as soon as they are completely defined, this happens before script execution. The import method is just a plain Perl sub.

s139252 fucked around with this message at 17:18 on Jan 2, 2009

Ninja Rope
Oct 22, 2005

Wee.
ToolSet might be helpful for this.

Mithaldu
Sep 25, 2007

Let's cuddle. :3:
satest4, thanks for the awesome explanation. I really should've realized that i need to export all functions that i want to carry over and i also learned something about the specifics of begin blocks. :)

As for "use base", i honestly really don't get what you're trying to say there. I DID notice that it didn't work for transporting simply exported functions, but i don't have any idea why, since i never looked into how OO internals work in Perl. However, it really IS part of the solution for my problem here, since CGI::Application plugins attach their functions to the caller when imported and thus these functions survive the "use base" transition. I've basically made an extended version of Titanium. (And i really should've looked at its source earlier, since it's really loving simple.)


My biggest thanks however go to Ninja Rope, since that is EXACTLY what i was looking for, and then some. :)

leedo
Nov 28, 2000

Has anyone else been playing around with perl6 the past few weeks?

I started writing a console version of Risk in p6, and was very surprised by how much is working. I got most of the classes set up with the appropriate properties and methods, and it all compiles cleanly.

Right now I am putting off figuring out the best way to draw a undirected graph to the console. Does anyone know of a good example I can go off of? I did a few searches on CPAN and found very little.

leedo fucked around with this message at 20:34 on Jan 4, 2009

Mario Incandenza
Aug 24, 2000

Tell me, small fry, have you ever heard of the golden Triumph Forks?
Without having looked at the code and judged its portability to p6, App::Asciio might be a good starting point.

Rakudo is definitely way faster than Pugs was, though.

Div
May 27, 2001

Triple Tech posted:

Someone from #cobol told me the problem I'm trying to solve at work is called "dependency injection", and it seems to be right on the money.

Which would be the least evil way of accessing a hash our'ed by a package? eval or a symbolic reference?

I'm trying to implement what I would call data inheritance... Like the data is structured similarly in all the subclasses, but I don't want to expend the energy (hurf) in creating accessor stubs in each subclass, since they're exactly the same.

Edit: I realize this is a bit all over the place...
Use Moose to add meta data to the classes.

s139252
Jan 1, 1970
test

Mithaldu posted:

As for "use base", i honestly really don't get what you're trying to say there.

Sorry. I was just pointing out that often it is desirable to import symbols into a package rather than rely on method resolution to get similar effect.

leedo
Nov 28, 2000

SpeedFrog posted:

Without having looked at the code and judged its portability to p6, App::Asciio might be a good starting point.

Rakudo is definitely way faster than Pugs was, though.

That is perfect, thanks! Should give me something to do for the next few weeks.

TiMBuS
Sep 25, 2007

LOL WUT?

I've been regularly checking out parrot and playing with it. Rakudo isn't bad and is a lot more complete than many realize but even so, writing whole apps in it always ends up running into weird annoying bugs and frustration. I mostly just use parrot to hammer out my toy language instead of trying to work on perl6.

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
Turns out I won't be attending either of Frozen or YAPC (even though our lovely Sartak is presenting at Frozen!! :neckbeard:). I'm lazy, it costs money, my company is just hemming and hawing... And, I had some other poo poo scheduled on those days as well. Sucks. I guess I have to put being an uber Perl nerd on hold for another year. Unless there other NA conferences I should know about. The cheaper the better, it seems.

leedo
Nov 28, 2000

I don't think it gets much cheaper than YAPC. I paid for it out of pocket last year when it was in Chicago. Well worth it, IMO.

Filburt Shellbach
Nov 6, 2007

Apni tackat say tujay aaj mitta juu gaa!
Vincent Pit (author of Variable-Magic) is a beautiful man.

He's been working on Scope-Upper which lets you gently caress with the stack. The feature for which Scope-Upper was written was the ability to use local on higher stack frames. This lets you abstract away syntax that was previously unabstractable!

For example, in testing functions you write, you're supposed to include at the top local $Test::Builder::Level = $Test::Builder::Level + 1; to tell Test-More how to find the line of code that actually defines the test. This way when it tells you that a test failed, Test-More can report the file and line number of that test. With Scope-Upper, Test-More could now give you a function to call that manages $Test::Builder::Level for you - abstracting away that call to local. Yes, this is pretty esoteric, but it does let you do more interesting things without having to burden your users with twiddling a dynamic variable.

Scope-Upper 0.04 and later give you a new procedure called unwind. This lets you return values to upper scopes! This is equivalent to an escape continuation. I'm pretty sure it's implemented reusing Perl's eval/die exception mechanism, so I trust that it properly cleans up after itself.

I've wanted both of these features for a long time. Now I can build more interesting syntax. I've started tonight with Continuation-Escape which gives you a nicer interface to unwind:

code:
use Continuation::Escape;

my $result = call_cc {
    my $escape = shift;

    sub { $escape->("escaped!") }->();

    exit; # This code will never be reached!
};

# $result is "escaped!"
It turns out that Vincent's Variable-Magic can hook into getting and setting values from the symbol table! (you can't tie the symbol table, but now with Variable-Magic we can have the same functionality you'd get with tie)

I love this guy!

Filburt Shellbach fucked around with this message at 02:45 on Jan 13, 2009

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
My mind hurts. What's a super practical use for this? Or what properly demonstrates the power/concision tradeoff?

Filburt Shellbach
Nov 6, 2007

Apni tackat say tujay aaj mitta juu gaa!
I can't remember any particular examples for where this would be useful. In general, if you have a complicated calculation involving many functions, you may want to return from an inner function without having to bubble that return through all the outer functions. As a trivial example..

code:
sub product {
    my @factors = @_;

    call_cc {
        my $escape = shift;

        my $product = 1;
        for my $factor (@factors) {
            $escape->(0) if $factor == 0;
            $product *= $factor;
        }

        return $product;
    };
}
In this example, you know that once you hit a zero the entire result will be zero, so you can return early without finishing the multiplication. This example is a little too trivial to really show why escape continuations are useful, but until I realize why I needed them again, that's all I've got. :shobon:

edit: I've remembered a recent reason why I wanted them. In Path-Dispatcher, you can "run" a "dispatch" which basically executes a list of code references. Any coderef can say "abort the entire run" or "skip to the next coderef". I use exceptions to implement this, but since neither is an exceptional case, they should be implemented with escape continuations. Once Scope-Upper is a little more vetted I'll do just that.

Filburt Shellbach fucked around with this message at 03:16 on Jan 13, 2009

Adbot
ADBOT LOVES YOU

biznatchio
Mar 31, 2001


Buglord
What's it do that this doesn't do?

code:
sub call_cc (&)
{
        my $clause = shift;
        my @fake_returns;
        my $fake_continuation;
        $fake_continuation = sub {
                @fake_returns = @_;
                die $fake_continuation;
        };
        eval {
                return $clause->($fake_continuation);
        };
        if ($@)
        {
                die $@  unless $@ == $fake_continuation;
                return @fake_returns;
        }
}
edit:

Triple Tech posted:

My mind hurts. What's a super practical use for this? Or what properly demonstrates the power/concision tradeoff?

It's just a way of wrapping eval/die to be able to use it to return a value from a block instead of just an exception.

For example, I could run a call_cc block, pass it's $escape ref into a sub, then pass it into a sub from inside there. I'm now two stack frames removed from the call_cc block, but I can still immediately return from the call_cc block to its caller by calling the $escape ref -- the exact same way die would unwind the stack, but the call_cc block returns normally instead of bubbling the die up the stack further.

Suppose you were trying to parse a string with a recursive descent parser, and you head down some speculative parse branch several subs deep before finally realizing the parse branch doesn't work out. You can escape out some sort of failure return value without needing to write eval/die handling logic at the original callsite.

biznatchio fucked around with this message at 19:38 on Jan 13, 2009

  • Locked thread