◆ 変数宣言構文がない言語だと 代入したときに外側を書き換えれば良いのか ローカルスコープに変数作れば良いのかがわからない
◆ use した変数は一種の引数みたいな扱いにして クロージャを定義した場所のスコープの変数は見れなくすれば 迷うことはなくなる

PHP の use

PHP ってクロージャを定義しても よくあるレキシカルスコープではなくて自分で use を使って渡した変数のみが使用可能という特殊なものです
よくある仕様じゃなくて独自の使いづらい仕様にするのはいつもの PHP なのでもう何も言いませんが どうしてこうしたのかは気になります

$value = 10;
$result = array_map(function($e){
return $e * $value;
}, [1, 2]);
print_r($result);

これは $value を参照できないので

PHP Notice: Undefined variable: value

と Notice が出ます
use を使うことで参照できます

$value = 10;
$result = array_map(function($e) use($value){
return $e * $value;
}, [1, 2]);
print_r($result);
Array
(
[0] => 10
[1] => 20
)

use だけでは参照渡しではなく引数の値渡しと同じです
書き換えても関数の中だけで use で指定した変数の方は変わりません
use したものがオブジェクトで そのプロパティを書き換えた場合は変わります

宣言がないから?

JavaScript だとこう書けます

const value = 10
const result = [1, 2].map(e => {
return e * value
})
console.log(result)
// [10, 20]

外側の value は気にせず関数の中に value という変数を新たに作りたい場合はこうなります

let value = 10
let result = [10, 20].map(e => {
let value = 100
return e * value
})
console.log(value, result)
// 10  [1000, 2000]

let がないと

value = 10
result = [10, 20].map(e => {
value = 100
return e * value
})
console.log(value, result)
// 100 [1000, 2000]

value が外側を書き換えてしまいます

PHP だと let や var にあたる変数宣言キーワードはありません
最初に代入すればそこで変数が作られます

そうなると クロージャ内での代入は新しくスコープ内に変数を作るのか 外側を書き換えるのかわかりづらく混乱します
完全にスコープを切り離して use を使って渡した変数のみ参照可能にして 外側を見れないとしてしまえば 代入時にどうなるかはわかりやすくなります

こういう理由で use 使うことになったのかなと思っています
使いづらいですけど

他の言語は

考えてみれば 宣言キーワードの要らない言語って PHP の他にもあると思います
他の言語はどうなんだろうとみてみました

JavaScript

JavaScript は宣言なしにもできますが 基本は var/let/const を書きます
書かない場合はグローバルスコープになるので 宣言なしではクロージャ内のローカルスコープに変数は作れません

静的スコープ上に存在→それを書き換え
存在なし→グローバルに代入

となります

Lua

JavaScript と同じで宣言なしはグローバルスコープの書き換えです
ローカルスコープにするには local キーワードを使います

Python3

Python だと関数内関数とラムダ式があります
関数内関数の場合は 代入はローカルスコープになります

def fn():
value0 = 1
value1 = 2
def sub():
value1 = 3
value2 = 4
print(value0, value1, value2)
sub()
print(value1)

fn()
# 1 3 4
# 2

value0 の 1 は参照できますが value1 で書き換えても外側は 2 のままです
また value2 は外側に存在しないので参照できません


ラムダ式は関数の body に式をひとつだけを書けるものです
Python では代入は式ではなく文なので変数を代入することはできません

value = 10
result = map(lambda e: e * value, [1, 2])
print(list(result))
# [10, 20]

こうすれば一応それらしいことは可能です
def let(v, fn):
return fn(v)

value = 10
result = map(lambda e: let(100, lambda value: e * value), [1, 2])
print(list(result))
# [100, 200]

Ruby

value = 10
result = [1, 2].map { |e|
e * value
}
p result
# [10, 20]

Ruby だと外側のスコープに変数があればそこを書き換えます

value = 10
result = [1, 2].map { |e|
value = 100
e * value
}
p result
# [100, 200]
p value
# 100

; の後に変数名を指定することで ローカルスコープの変数を宣言することもできます

value = 10
result = [1, 2].map { |e; value|
value = 100
e * value
}
p result
# [100, 200]
p value
# 10

こうすればローカルスコープになるので外側は書き換わりません
ただし先にローカルスコープに変数を宣言するので 代入前に参照しても外側の値を参照できません

R

value = 10
result = Map(function(x) x * value, 1:2)
print(result)
[[1]]
[1] 10

[[2]]
[1] 20

代入はローカルスコープで外側には影響しません

value = 10
result = Map(function(x){
value = 100
x * value
}, 1:2)
print(result)
print(value)
[[1]]
[1] 100

[[2]]
[1] 200

[1] 10

ローカルスコープではなく外側の値を更新したい場合は 代入演算子を変えます

value = 10
result = Map(function(x){
value <<- 100
x * value
}, 1:2)
print(result)
print(value)
[[1]]
[1] 100

[[2]]
[1] 200

[1] 100

Julia

Julia は JavaScript や Lua に近いですが 基本は宣言不要です
書かなくてもグローバルにはなりません
必要なときのみ global, local キーワードつけます

value = 10
result = map(e -> e * value, [1, 2])
println(result)
# [10,20]
println(value)
# 10

外側に変数があれば外側を書き換えます

function fn()
value = 10
result = map(function(e)
value = 100
e * value
end, [1, 2])
println(result)
println(value)
end

fn()
# [100,200]
# 100

local を使うことで外側に同名の変数があっても新しくスコープに変数を作ります

function fn()
value = 10
result = map(function(e)
local value = 100
e * value
end, [1, 2])
println(result)
println(value)
end

fn()
# [100,200]
# 10

Elixir

value = 10
result = Enum.map([1, 2], fn(e) -> e * value end)
IO.inspect result
# [10, 20]

value = 10
result = Enum.map([1, 2], fn(e) ->
value = 100
e * value
end)
IO.inspect result
# [100, 200]
IO.inspect value
# 10

変数は immutable なので外側を書き換えることは出来ずローカルスコープになります
mutable 変数がないのでたぶん外側を書き換えることはできません

Hack

PHP と似ている Hack ですが function でクロージャを作ると PHP と一緒で use を使います
<?hh

$value = 10;
$result = (vector {1, 2})->map(function($e) use ($value){
return $e * $value;
});
print_r($result);
HH\Vector Object
(
[0] => 10
[1] => 20
)

Hack にはラムダ式もあります
こっちだと use なしで外側を参照できます

<?hh

$value = 10;
$result = (vector {1, 2})->map($e ==> {
return $e * $value;
});
print_r($result);
HH\Vector Object
(
[0] => 10
[1] => 20
)

内側で代入してもローカルスコープになり 外側は変わりません

<?hh

$value = 10;
$result = (vector {1, 2})->map($e ==> {
$value = 100;
return $e * $value;
});
print_r($result);
print_r($value);
HH\Vector Object
(
[0] => 100
[1] => 200
)
10

Hack のラムダ式は PHP の use で変数をキャプチャするのを楽にしてくれるもので ラムダ式が暗黙的に use して変数を使えるようにしてくれます
しかし use は値としてのキャプチャのみで 参照してのキャプチャはサポートされていません
外側を書き換える必要があるなら function を使う構文で use を自分で書いて参照にする必要があります
https://docs.hhvm.com/hack/lambdas/introduction

まとめ

ローカルスコープになるのか外側のスコープになるのか 言語ごとにバラバラでしたね
好きな方を選べるものは便利ですが どっちがどっちだっけと思うこともあります
PHP の use だと 外側と切り離されてるのがすぐにわかるのでローカルスコープだとほぼ迷うことがないというメリットはあるとおもいます
普段書くのがすごく面倒ですけど