1. Over View
どもども、番外編その2であります
今回、似てるけど、動的型付け vs 静的型付けの話じゃない、ただのポエムであります。
さて、変数のスコープと言えば、変数がアクセス可能な範囲を指します。常識ですね。
これがですね、C言語やJavaで飯を食ってると、変数宣言をして、その変数を使う、ってのが常識な訳でして。
Cでstaticで変数なんか宣言して、別のモジュールから呼び出してみなさいな、IDEだのエディタの限界みたいな
レビューをする羽目になります。
一方、こう言う書き方嫌いだったのね。
for( int i = 0; i < LOOP_MAX ; i++ ) {
iを使った処理
}
変数宣言にコメントが無くて、わかりにくい。反面、下の行見りゃ、iがループの変数だってわかるよね?
その変数が上のforのブロック内で使い終わることわかってるのに、外に出す必要がある?
…などと、ハリネズミのジレンマ的に、自分自身にちくちく来るわけですよ。
なんで、今回は「自分が納得しました」と言う話をしたいと思います。
2. Elixirの変数のスコープについて
以下の三つみたいですね。
スコープの種類 | 説明 |
---|---|
関数スコープ | 関数内で定義された変数は、その関数内でのみアクセス可能です。関数が終了すると、変数はスコープ外となり、アクセスできなくなります |
ブロックスコープ | ブロック(例えば、ifやcaseなど)の内部で定義された変数は、そのブロック内でのみアクセス可能です。ブロックが終了すると、変数はスコープ外となります |
モジュールスコープ | モジュール内で定義された変数は、そのモジュール内でのみアクセス可能です。モジュール外からはアクセスできません |
2.1 関数スコープ
まずは、関数の中でしか、変数は使えないと言うお話
defmodule Example do
def my_function do
x = 10
IO.puts(x) # 出力: 10
end # endで終わっているので、xのスコープはここでおしまい。
def another_function do
IO.puts(x) # エラー: xは未定義
end
end
ほら、怒られたw
warning: redefining module Example (current version defined in memory)
iex:18: Example (module)
error: undefined variable "x"
iex:24: Example.another_function/0
** (CompileError) iex: cannot compile module Example (errors have been logged)
(elixir 1.15.4) src/elixir_module.erl:182: anonymous fn/9 in :elixir_module.compile/7
iex:18: (file)
2.2 ブロックスコープ
これ、要注意ね。いや、例見りゃ納得出来るだろうけど。
defmodule Example do
def my_function do
if true do
y = 20 # ここでyが初めて使用されてます。
IO.puts(y) # 出力: 20
end # endで終わっているので、yのスコープはここでおしまい。
IO.puts(y) # エラー: yは未定義
end
end
if true do ~ endの内部のブロックで、yが初めて使われております。
なので、endの後には使えませんわな。
ちゃんとエラーになるのを確認してます。
warning: redefining module Example (current version defined in memory)
iex:18: Example (module)
error: undefined variable "y"
iex:24: Example.my_function/0
** (CompileError) iex: cannot compile module Example (errors have been logged)
(elixir 1.15.4) src/elixir_module.erl:182: anonymous fn/9 in :elixir_module.compile/7
iex:18: (file)
2.3 モジュールスコープ
これはわかりやすいよね。
defmodule Example do
@module_var 30
def my_function do
IO.puts(@module_var) # 出力: 30
end
end # ここで、 @module_varはスコープがおしまい。
IO.puts(Example.@module_var) # エラー: @module_varは未定義
もちろん、エラーなります。
iex(20)> IO.puts(Example.@module_var) # エラー: @module_varは未定義
error: undefined variable "module_var"
iex:20
** (CompileError) cannot compile code (errors have been logged)
3. 本日の心の合点ボタン
Elixirで、「変数宣言がなくても、俺自身がどこで解放されてりゃいいか、分かりやすくするコードを意識すればいいんじゃないかな?」と意識を転換しました。
そう気づいた理由が、無名変数の省略記法でして。
これが普通の書き方
sum = fn (a, b) -> a + b end
sum.(2, 3)
5
省略記法
sum = &(&1 + &2)
sum.(2, 3)
5
まー、ファンクション宣言が&()になっちゃうのは驚きましたが、確かに引数、&1や&2でも困らないよね。無名関数なんてのは短く書くもんだしさ。
であれば、変数名もスコープ内で使うものは、別のスコープなら無視すりゃいいんだから、無理に変数宣言にこだわらんでもよくね?
と思った次第です。