Perlにおける意外と便利な local

この記事は、 [Perl Advent Calendar](https://qiita.com/advent-calendar/2018/perl) の9日目の記事です。

# Perlにおける`local`とは一体何なのか？

まずは Perl Doc を読むところからでしょう。

> You really probably want to be using my instead, because local isn't what most people think of as "local". See Private Variables via my() in perlsub for details.

https://perldoc.perl.org/functions/local.html

**え、 `my` を使いたいんだろ？って書いている.....**

そうなんです、Perlにおいて、（レキシカルスコープにおける）局所変数を宣言する場合には、 `my` を使うべきですし、大凡全ての場合において、 `my` を使うことにこだわるべきなのです。
では、 `local` って一体何なのでしょうか？

# レキシカルスコープとダイナミックスコープ

`my` と `local` の大きな違いは、それがレキシカルスコープなのか、ダイナミックスコープなのかという点です。

まず、2つの共通点は、宣言したスコープの外からは参照出来ないということです。

そして、2つが異なる点は、その宣言の意味と、有効なスコープです。

まず、 `my` は宣言したブロック内でのみ有効な局所変数を宣言します。
一方、 `local` は局所変数を宣言するのではなく、グローバル変数(パッケージ変数)に対して、ブロック内で有効な一時的な値を与えるのに使用されます。

そして、`my` の有効範囲は、宣言したブロック内（字面のみから静的に決まるのでレキシカルスコープ）なのと比較して、 `local` の有効範囲は宣言したブロックから呼び出される全てのコード（実行時に動的に決まるのでダイナミックスコープ）になります。

例えば、以下のコードを実行すると、 `local $bar` が宣言されたスコープ内で `foo` が実行された時のみ、 `local bar` が出力されます。

```perl
use strict;
use warnings;
use utf8;
use feature qw/say/;

our $bar = 'our bar';

sub foo {
    say $bar; # This references a package variable.
}

{
    foo(); # もともとの `our bar` が出力される
}

{
    my $bar = 'my bar';
    foo(); # `foo()` 内のコードは、レキシカルスコープ外なので "our bar" が出力される
}

{
    local $bar = 'local bar';
    foo(); # `foo()` 内のコードは、ダイナミックスコープ内なので、 "local bar" が出力される
}

{
    foo(); # `local` 宣言されたダイナミックスコープの外なので、もともとの "our bar" が出力される
}

```

通常、実行時に参照される値を変えたいような需要に関しては、サブルーチンの引数を使う等の分かりやすい手段が存在する為、`local` の出番はありません。
では、どんなときに、このダイナミックスコープが便利になるのでしょうか？

# 便利な部分の考察

まず、今でも `local` を使うべき 3箇所については、 perldoc に書かれているので是非参照してください。基本的には **一時的に** 値を変更したい場合に使う形になっています。

http://perldoc.jp/docs/perl/5.6.1/perlsub.pod#When32to32Still32Use32local40-41

では、どうして、そのような場合に `local` が便利になるのかを考えてみます。

# 便利その1: スコープを抜けた際に、必ず元の状態に復帰することが保証されている

`local` の強力な点は、それが言語レベルでサポートされているということです。
例えば、一時的に `$Foo::CONFIG` を書き換える例を見てみましょう。（一時的に書き換えるということは、他の処理に影響を及ぼさないように、戻す必要があります）

```perl
{
    package Foo;
    our $CONFIG = {};
}

# local を使った場合

{
    local $Foo::CONFIG{AAA} = 'BBB';
    do_something();
}

# local を使わない場合
{
    my $prev_aaa = $Foo::CONFIG{AAA};
    $Foo::CONFIG{AAA} = 'BBB';
    eval { do_something(); };
    $Foo::CONFIG{AAA} = my $prev_aaa;
    if ($@) {
      die $@;
    }
}
```

みて頂けると分かるように、 `local` を使った場合の方が、非常に簡潔な記述が可能です。
特に、「スコープを抜けた際に、必ず元に戻る」ということが言語レベルでサポートされているため、例外発生時の処理も含めて簡潔で安全な運用が可能です。特に、 `%SIG` 等について一時的な値を設定する場合などについては、顕著になると思います。

# 便利その2: 呼び出し先がどれだけ深くとも、キチンと挙動を変更出来る

`local` の便利な点としては、それがダイナミックスコープであるため、そこから呼ばれる如何なるコードに対しても、影響を及ぼすことが可能だという点です。(引数を引き回して状態を伝えていく必要が無い)

例えば、以下のコードを見てみましょう。
これは、一時的に `INT` による割り込みを無効にして、 `Foo::bar` のサブルーチンを書き換えて、非常に重い処理をするようなコードになっています。

```perl
{
    no strict qw/refs/;
    no warnings qw/redefine/;
    local $SIG{INT} = 'IGNORE';
    local *Foo::bar = sub { 'local bar' };
    some_really_heavy_procedures();
}
```

ダイナミックスコープを利用することで、 `some_really_heavy_procedures` の中身がどのように実装されているかに関わらず、 `INT` による割り込みの無効化、および、 `Foo::bar` サブルーチンの変更が全てに適用されます。
例えば、 既存コードの `some_really_heavy_procedures` の内部の様々な局面で `Foo::bar` が呼ばれるような実装になっており、綺麗な設計に変更するコストが非常に高い場合には、トップレベルでの黒魔術が功を奏す場合もあります。（`$SIG{INT}`は黒魔術とかではなく、必ず`local`を使うべきものです。そして、黒魔術を使っているということが、誰の目にも明らかになるような設計にはしましょう）

# まとめ

ということで、  local の意外に便利な側面を見てきました。
繰り返しになりますが、殆どの場合において、我々に必要なものは `my` であって、 `local` ではありません。
が、時として、 `local` が非常に便利に使える局面もあります。
用法用量を適切に守りさえすれば、非常に便利に使えるので、選択肢の一つに入れることは良いのではないでしょうか？ 

**There's More Than One Way To Do It**

# 参考

もっとキチンと勉強したい方は、是非 perldoc を参照して頂けると助かります。

https://perldoc.perl.org/perlvar.html
https://perldoc.perl.org/functions/local.html
https://perldoc.perl.org/perlsub.html#Temporary-Values-via-local()
https://perldoc.perl.org/perlsub.html#When-to-Still-Use-local()