7

I am wondering about how I can do in Perl what I commonly do in lisp:

(defvar *verbose-level* 0)
(defun my-function (... &key ((:verbose-level *verbose-level*) *verbose-level*) ...) ...)

this means that my-function is run at the current level of verbosity, but I can pass it a different level which will affect all its calls too:

(defun f1 (&key ((:verbose-level *verbose-level*) *verbose-level*))
  (format t "~S: ~S=~S~%" 'f1 '*verbose-level* *verbose-level*)
  (f2 :verbose-level 1)
  (format t "~S: ~S=~S~%" 'f1 '*verbose-level* *verbose-level*)
  (f2 :verbose-level (1+ *verbose-level*))
  (format t "~S: ~S=~S~%" 'f1 '*verbose-level* *verbose-level*))
(defun f2 (&key ((:verbose-level *verbose-level*) *verbose-level*))
  (format t "~S: ~S=~S~%" 'f2 '*verbose-level* *verbose-level*))
[17]> (f1)
F1: *VERBOSE-LEVEL*=0
F2: *VERBOSE-LEVEL*=1
F1: *VERBOSE-LEVEL*=0
F2: *VERBOSE-LEVEL*=1
F1: *VERBOSE-LEVEL*=0
NIL
[18]> (f1 :verbose-level 4)
F1: *VERBOSE-LEVEL*=4
F2: *VERBOSE-LEVEL*=1
F1: *VERBOSE-LEVEL*=4
F2: *VERBOSE-LEVEL*=5
F1: *VERBOSE-LEVEL*=4

(note that the variable bindings are restored on exit - even abnormal - from functions).

How can I do something like that in Perl?

E.g., in misc.pm, I have our $verbose=0;. How do I write a function which would bind $verbose to a value of its argument and restore its value on return?

sds
  • 58,617
  • 29
  • 161
  • 278

2 Answers2

10

Perl's concept of global variables is quite similar to special variables in CL.

You can “shadow” the value of a global variable with local:

our $var = 1;

func("before");

{
  # a block creates a new scope
  local $var = 2;
  func("inside");
}

func("after");

sub func { say "@_: $var" }

Output:

before: 1
inside: 2
after: 1

If you local a value, the new value is visible throughout the whole dynamic scope, i.e. in all functions that are called. The old value is restored once the lexical scope is left by any means (errors, returns, etc). Tail calls do not extend the dynamic scope, but count as scope exit.

Note that global variables have a fully qualified name. From a different package, you would do something like

local $Other::Package::var = 3;
Other::Package::func("from a package far, far away");

This is commonly used to provide configuration for packages with a functional (non-OO) interface. Important examples are Carp and Data::Dumper.

amon
  • 57,091
  • 2
  • 89
  • 149
  • 1
    It is kind of funny to see the similarities between Perl and CL. Starting with the compilation model (why *shouldn't* code be executed during parsing?) over special vs. lexical vars up to seperate namespaces (CL: variables, functions, labels, streams, …; Perl: scalars, arrays, hashes, subs, IO, …). Oh, and CLOS/Moose are obviously related – amon Jun 20 '13 at 16:09
3

If I understand you correctly, you need to locally override a global variable inside a function.

package my_package;
our $verbose = 0;

sub function {
    my ($arg1, $arg2) = @_; # getting function arguments.
    local $verbose = $arg1;
}

It will restore old state of $verbose on return.

Karsten S.
  • 2,349
  • 16
  • 32
  • Thanks. What if `function` is called without arguments? I.e., `$arg1` is unbound. Do I need to resort to `local $verbose = defined $arg1 ? $arg1 : $verbose` or is there a prettier approach? – sds Jun 20 '13 at 16:12
  • You can write `local $verbose = $arg1 if defined $arg1;`. – Karsten S. Jun 20 '13 at 16:18
  • That doesn't make sense. If `$arg1` is not defined, then `$verbose` will be undefined. What you can do is `local $verbose = shift; $verbose = "default" if not defined $verbose;` (Or use the defined-or assignment operator: `$verbose //= "default"`). – TLP Jun 20 '13 at 19:00
  • Well, it does make sense - I checked it in a script. `$verbose` will be overridden locally only if `$arg1` is defined. Otherwise the `local $versbose = $arg1` isn't executed at all. – Karsten S. Jun 20 '13 at 19:02