VBAの関数名とモジュール名修飾で起きる再帰を理解する
Functionの戻り値変数とプロシージャ呼び出しの境界を整理する
Copyright © 2026 LWP 山中 一弘
本資料は、出典を明記いただければ、商用・非商用を問わず、ご自由に複製・改変・再配布していただけます。なお、著作権表示は改変せず、そのまま記載してご利用くださいますようお願いいたします。
記事要約
VBAのFunctionプロシージャでは、戻り値を返すときに関数名へ値を代入します。この仕様を知っていると、Test = 1 のような行は「関数を呼んでいる」のではなく、「戻り値を設定している」と読めます。
ところが、同じ名前に標準モジュール名を付けて Module1.Test = 1 のように書くと、読み方が変わります。関数内の戻り値用変数に代入しているつもりでも、VBA側ではモジュール内のFunctionプロシージャを参照していると解釈され、結果として自分自身を呼び出す再帰に見える動きになります。
この記事では、VBAのFunction名が持つ二重の役割を整理します。関数名は、外側から見るとプロシージャ名ですが、Functionの内側では戻り値を保持する名前としても使われます。この二つを混同すると、標準モジュール名修飾を付けたときに、なぜ再帰が起きるのかが分からなくなります。
本記事の対象とゴール
想定読者
VBAのFunctionプロシージャの戻り値設定を、なんとなく関数名への代入として覚えている人
Module1.FunctionName のようなモジュール名修飾を使うと、なぜ挙動が変わるのか知りたい人
VBAの名前解決、戻り値変数、再帰呼び出しの境界を整理したい人
業務マクロのレビューで、標準モジュール名つきの呼び出しを安全に読めるようにしたい人
本記事で得られること
Function名が、プロシージャ名と戻り値用の名前という二つの顔を持つことが分かります。
Test = 1 と Module1.Test = 1 の読み方が違う理由を説明できます。
標準モジュール名修飾によって、戻り値代入のつもりがFunction呼び出しに寄る危険を理解できます。
VBAコードレビューで、不要なモジュール名修飾を避ける判断軸が得られます。
本記事で扱わないこと
VBAコンパイラやランタイムの内部実装の厳密な仕様解析
すべての名前解決規則の網羅
クラスモジュール、オブジェクト参照、Propertyプロシージャまで含めた詳細な呼び出し規則
配列戻り値のコピー最適化やVariant内部表現の完全な検証
先に結論
VBAのFunction内で、関数名だけに値を代入する行は、基本的には戻り値の設定として読むべきです。
一方で、標準モジュール名を付けて Module1.Test = 1 のように書くと、その名前はFunction内部の戻り値用変数ではなく、モジュールに属するプロシージャ名として解釈されやすくなります。その結果、左辺を評価するために Module1.Test を呼び出し、同じFunctionへ戻ってくる形になり、再帰が発生します。
安全な書き方は単純です。Functionの戻り値を設定するときは、Function内で関数名だけに代入します。戻り値設定のために標準モジュール名を付けません。
第1章 Function名は戻り値用の名前でもある
VBAのFunctionプロシージャを読むとき、最初に押さえるべきなのは、Function名の役割です。
1.1 外から見るFunction名
Function名は、外側から見るとプロシージャ名です。
たとえば Test() というFunctionがあれば、別のプロシージャから Test() と書いて呼び出せます。戻り値を使うなら、呼び出し結果を変数に代入します。
Dim result As Longresult = Test()この読み方では、Test は呼び出される処理の名前です。
1.2 内側から見るFunction名
Functionの内側では、同じ名前が戻り値の設定にも使われます。
Function Test() As Long Test = 1End Functionこの Test = 1 は、Test というFunctionを呼び出しているのではありません。Functionの戻り値へ 1 を設定しています。
Microsoft LearnのFunction文の説明でも、Functionから値を返すには、値をFunction名へ代入すると説明されています。つまり、VBAではFunction名が戻り値用の名前として働きます。
1.3 ここで混乱が生まれる
同じ Test という名前が、外側ではプロシージャ名、内側では戻り値用の名前として見えるため、読み手は文脈で判断する必要があります。
単独の代入式で Test = 1 と書かれていれば、Function内部では戻り値設定として読めます。しかし、ここに標準モジュール名を付けると話が変わります。
第2章 モジュール名修飾を付けると文脈が変わる
この章では、Test = 1 と Module1.Test = 1 の違いを整理します。
2.1 `Test = 1` は戻り値設定として読める
Function内部の次のコードは、戻り値設定として自然に読めます。
Function Test() As Long Test = 1End Functionこの場合、左辺の Test はFunctionの戻り値用の名前です。処理は自分自身を呼び出していません。
2.2 `Module1.Test = 1` は別の意味に寄る
同じFunction内で、次のように標準モジュール名を付けると、意味が変わります。
Function Test() As Long Module1.Test = 1End Function書き手は「Module1にあるTestの戻り値へ代入している」と考えるかもしれません。しかし、Function内部にある戻り値用の名前は、ローカルな戻り値の置き場所として見ればよいものです。標準モジュール名を付けて修飾する対象ではありません。
Module1.Test と書くと、VBAはモジュールに属する Test プロシージャを参照する方向に寄ります。そのため、左辺を評価する段階で Module1.Test を呼び出す形になり、結果として同じFunctionへ戻ってきます。
2.3 再帰が発生する構図
動きの構図は次のように整理できます。
Testが呼ばれる ↓Function内で Module1.Test = 1 を評価する ↓Module1.Test は戻り値用変数ではなく、Module1のTestプロシージャとして解釈される ↓Testが再び呼ばれる ↓同じ行へ戻る ↓再帰が続くこの再帰には、終了条件がありません。そのため、実行すればスタックを消費し続ける危険があります。
第3章 代入式だから安全とは限らない
= があるから代入であり、代入なら関数呼び出しではない、と考えると危険です。
3.1 左辺も評価される
代入式では、右辺だけでなく左辺も解釈されます。
単純な変数なら、左辺の評価は分かりやすいです。
x = 1この場合、x は変数です。しかし、左辺が Module1.Test のような修飾名になると、それが変数なのか、プロシージャなのか、オブジェクトのメンバーなのかをVBAが解釈します。
3.2 戻り値用の名前はローカルな文脈で読む
Functionの戻り値用の名前は、Function内部のローカルな文脈で読むべきです。
Function Test() As Long Test = 1End Functionこの Test は、Function外部の Module1.Test とは同じ文字列でも、読み方が違います。Function内で戻り値を設定するときは、モジュール名を付けずに、関数名だけを使います。
3.3 モジュール名修飾は呼び出し側で使う
標準モジュール名修飾は、外側からどのモジュールのプロシージャを呼ぶかを明示したいときに使うものです。
result = Module1.Test()このコードは、Module1 にある Test Functionを呼び出し、その戻り値を result に入れるという意味で読めます。
一方、Function自身の中で戻り値を設定する目的で Module1.Test = 1 と書くのは、戻り値設定ではなく呼び出しに寄った解釈を招くため、避けるべきです。
第4章 戻り値変数という見方を持つ
VBAのFunctionを安全に読むには、戻り値変数という見方を持つと整理しやすくなります。
4.1 Function名は不可視の戻り値変数のように使われる
VBAのFunction内では、関数名が戻り値の入れ物として使われます。
これは、明示的に次のような変数が宣言されているわけではありません。
Dim Test As Longしかし、読むときには、Function内に戻り値用の名前があると考えると理解しやすくなります。
Function Test() As Long Test = 1End Functionこの Test は、処理を呼ぶ名前ではなく、Functionが返す値を一時的に保持する名前です。
4.2 名前が同じでも、名前空間が同じとは限らない
Test という文字列が同じでも、どの文脈で解釈されるかによって意味が変わります。
Function外部の Test() は、Functionプロシージャの呼び出しです。
Function内部の Test = 1 は、戻り値の設定です。
Function内部の Module1.Test = 1 は、モジュール内プロシージャ参照として解釈され、再帰を招く可能性があります。
この違いを意識すると、VBAの名前解決で起きる不思議な挙動を、単なる怪奇現象ではなく、文脈の切り替わりとして読めます。
第5章 実務での書き方
実務では、挙動の面白さよりも、読めるコードにすることが重要です。
5.1 戻り値設定ではモジュール名を付けない
Function内で戻り値を設定するときは、関数名だけに代入します。
Function TotalAmount() As Currency TotalAmount = 1000End Functionこの形なら、戻り値設定であることが分かります。
避けるべきなのは次の形です。
Function TotalAmount() As Currency Module1.TotalAmount = 1000End Functionこのコードは、戻り値設定として読みにくく、意図しない再帰や名前解決の混乱を招きます。
5.2 呼び出し側では必要なら修飾する
外側から呼び出すときは、名前衝突を避けるためにモジュール名を付けることがあります。
amount = SalesCalc.TotalAmount()これは、どのモジュールのFunctionを呼ぶかを明示するための修飾です。戻り値設定のための修飾ではありません。
5.3 コードレビューで見るポイント
VBAコードをレビューするときは、次を確認します。
Function内で、戻り値設定にモジュール名修飾を付けていないか。
関数名と変数名が読み手に混同される形になっていないか。
モジュール名修飾が、呼び出し側の名前衝突回避として使われているか。
再帰するFunctionに終了条件があるか。
意図しない自己呼び出しが発生する書き方になっていないか。
第6章 今回のまとめから得られる判断軸
今回の元素材では、標準モジュール名を付けてFunction名へ代入すると再帰が発生する、という不思議な挙動が話題になっていました。
この現象は、次のように整理できます。
Function名は、外側ではプロシージャ名である。
Function内部では、同じ名前が戻り値設定にも使われる。
関数名だけの代入なら、戻り値設定として読める。
モジュール名修飾を付けると、戻り値用の名前ではなく、モジュール内プロシージャ参照として解釈されやすい。
その結果、左辺評価の過程で同じFunctionを呼び出し、再帰が発生する。
VBAでは、名前の文字列だけを見ても意味は決まりません。どの文脈で使われているか、左辺なのか右辺なのか、Function内部なのか外部なのか、モジュール名修飾が付いているのかを分けて読む必要があります。
まとめ
VBAのFunction名は、プロシージャ名であり、同時に戻り値を設定するための名前でもあります。
Function内部で Test = 1 と書けば、通常は戻り値設定として読めます。しかし、Module1.Test = 1 のように標準モジュール名で修飾すると、その名前は戻り値用のローカルな名前ではなく、モジュールに属するFunctionプロシージャとして解釈されやすくなります。
そのため、左辺を評価する段階で同じFunctionが呼び出され、終了条件のない再帰になります。
実務上の結論は単純です。Functionの戻り値を設定するときは、Function内で関数名だけに代入します。モジュール名修飾は、呼び出し側でどのモジュールのプロシージャを呼ぶかを明示するために使います。
この区別を持っておくと、VBAのFunction、戻り値、名前解決、再帰の関係を落ち着いて読めるようになります。
出典メモ
元URL: https://togetter.com/li/1831811
正規URL: https://posfie.com/@hoehoe1234/p/gY4s6jF
元タイトル: 2022-01-16 VBA 標準モジュール名修飾で再帰発生
取得日: 2026-06-11
原型として使った範囲: posfieページのタイトル、説明、公開投稿12件の論点。投稿本文は丸写しせず、Function名、戻り値用名前、モジュール名修飾、再帰発生の論点として再構成した。
公式確認: Microsoft Learn Function statement (VBA)、Calling Sub and Function procedures (VBA)。
