Emulate perls local

K

Karsten Meier

Hello Ruby Fans,

I'm working on the ruby section of the pleac project,
(http://pleac.sourceforge.net) because I think a good way to learn a new
language is trying to explain it to somebody else.
There are a few hundred code snippnets from the famous perl cookbook,
and one of it is perls local function:
You can give a global variable inside a block a new value. This is
slightly different from normal local variable, because it really changes
the global variable. I think it is sometimes even useful, when you need
to change the predefined global variables, but want to automatically
restore them after you are done.
The phyton documenter said that it is not possible in phyton.
But this is ruby, so there should be a solution.
I came up with the following:

def local(var)
eval("save = #{var.id2name}")
result = yield
eval("#{var.id2name} = save")
result
end

# example usage

def print_age
puts "Age is #{$age}"
end

$age = "to small"
print_age()
local:)$age){
$age = 23
print_age()
}
print_age()

It prints the following:
to small
23
to small

It works quite well, but I like to know if I can get rid of the eval().
I already have my variable as symbol, so I think it must be possible
somehow to restore the variable from the symbol, without use of eval.
Do you have an idea for a more elegant solution?

Regards
Karsten Meier
Hamburg * Germany
 
R

Robert Klemme

Newsbeitrag
Not really.

rpolzer@katsuragi ~ $ perl -e '
$a = 17;
$p = \$a;
{
local $a = 42;
$q = \$a;
}
print "$a, $p<$$p>, $q<$$q>\n";
'
17, SCALAR(0x812e798)<17>, SCALAR(0x812155c)<42>

It's more than Save&Restore of the variable contents. It saves and
restores the variable pointers in the symbol table. In fact, both
$a were DIFFERENT variables, which justifies the "declaration-style
syntax" of local.

But perl and ruby treat variables differently. Especially in perl there
can be two instances that carry the value 42 while in ruby this is always
one instance that is referenced. If you would use the proposed solution
and assign another global from the temporarily changed global you could
produce similar output to your perl variant. (Without the hex addresses,
of course)

My concern would rather be that the proposed solution (apart from the
exception thingy that you fixed) would not work for multiple threads. As
far as I remember the perl's "local" does dynamically scoping, i.e., it
affects only methods invoked from the method that contains the "local". In
a single threaded environment this is no difference, but in MT
environments as ruby this does make a significant difference. Thus a
thread safe ruby solution should rather include Thread[] and Thread[]=,
which unfortunately would make the syntax more ugly.

Technically this could be a solution, but it does not look nice which
certainly defys the original intention:

def local(sym, val)
ar = ( Thread.current[sym] ||= [] )
ar.unshift( val )

begin
yield
ensure
ar.shift
end
end

def get_local(sym)
ar = Thread.current[sym]
raise "#{sym} undefined" unless ar && !ar.empty?
ar[0]
end

def set_local(sym, val)
ar = Thread.current[sym]
raise "#{sym} undefined" unless ar && !ar.empty?
ar[0]= val
end

# usage, thread safe
local :foo, 20 do
puts get_local( :foo )

local :foo, 42 do
puts get_local( :foo )
end
end

Regards

robert
 
K

Karsten Meier

Thank you for the feedback.
(Are only germans interested in this kind of stuff?)
I think most usages in perl just don't make sense in ruby,
normally it is better to use a local variable, you also have no trouble
to put file handles in a variable in ruby.
It might be useful for the predefined global variables, like
$/ input record separator
$\ output record separator
The perl cookbook mentions also it is possible to use it for temporary
signal handlers, like this (perl code)
local $SIG(INT) = 'IGNORE';
I never needed to install a signal handler at all, so I'm not sure
if this something intersting, and if it apply to ruby.


The exception hint is very good, because I think such a local
function is only useful if the control flow is quite complex.
Otherwise it easier just to save the var and restore it later.

It is true that we have problems if we are dealing with threads,
but I think if you are using threads and manipulate globals at the same
time, you have trouble anyway. The code is correct in the sense that
the global has temporarly a new variable, so all called methods sees
this new value, and also all other threads see this variable.
but it might not be what you expected.

Karsten Meier
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top