VBAの関数名とモジュール名修飾で起きる再帰を理解する

VBAの関数名とモジュール名修飾で起きる再帰を理解する

Functionの戻り値変数とプロシージャ呼び出しの境界を整理する

Copyright © 2026 LWP 山中 一弘

本資料は、出典を明記いただければ、商用・非商用を問わず、ご自由に複製・改変・再配布していただけます。なお、著作権表示は改変せず、そのまま記載してご利用くださいますようお願いいたします。

記事要約

VBAのFunctionプロシージャでは、戻り値を返すときに関数名へ値を代入します。この仕様を知っていると、Test = 1 のような行は「関数を呼んでいる」のではなく、「戻り値を設定している」と読めます。

ところが、同じ名前に標準モジュール名を付けて Module1.Test = 1 のように書くと、読み方が変わります。関数内の戻り値用変数に代入しているつもりでも、VBA側ではモジュール内のFunctionプロシージャを参照していると解釈され、結果として自分自身を呼び出す再帰に見える動きになります。

この記事では、VBAのFunction名が持つ二重の役割を整理します。関数名は、外側から見るとプロシージャ名ですが、Functionの内側では戻り値を保持する名前としても使われます。この二つを混同すると、標準モジュール名修飾を付けたときに、なぜ再帰が起きるのかが分からなくなります。

本記事の対象とゴール

想定読者

  • VBAのFunctionプロシージャの戻り値設定を、なんとなく関数名への代入として覚えている人

  • Module1.FunctionName のようなモジュール名修飾を使うと、なぜ挙動が変わるのか知りたい人

  • VBAの名前解決、戻り値変数、再帰呼び出しの境界を整理したい人

  • 業務マクロのレビューで、標準モジュール名つきの呼び出しを安全に読めるようにしたい人

本記事で得られること

  1. Function名が、プロシージャ名と戻り値用の名前という二つの顔を持つことが分かります。

  2. Test = 1 と Module1.Test = 1 の読み方が違う理由を説明できます。

  3. 標準モジュール名修飾によって、戻り値代入のつもりがFunction呼び出しに寄る危険を理解できます。

  4. 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 Long
result = Test()

この読み方では、Test は呼び出される処理の名前です。

1.2 内側から見るFunction名

Functionの内側では、同じ名前が戻り値の設定にも使われます。

Function Test() As Long
    Test = 1
End 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 = 1
End Function

この場合、左辺の Test はFunctionの戻り値用の名前です。処理は自分自身を呼び出していません。

2.2 `Module1.Test = 1` は別の意味に寄る

同じFunction内で、次のように標準モジュール名を付けると、意味が変わります。

Function Test() As Long
    Module1.Test = 1
End 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 = 1
End 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 = 1
End 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 = 1000
End Function

この形なら、戻り値設定であることが分かります。

避けるべきなのは次の形です。

Function TotalAmount() As Currency
    Module1.TotalAmount = 1000
End Function

このコードは、戻り値設定として読みにくく、意図しない再帰や名前解決の混乱を招きます。

5.2 呼び出し側では必要なら修飾する

外側から呼び出すときは、名前衝突を避けるためにモジュール名を付けることがあります。

amount = SalesCalc.TotalAmount()

これは、どのモジュールのFunctionを呼ぶかを明示するための修飾です。戻り値設定のための修飾ではありません。

5.3 コードレビューで見るポイント

VBAコードをレビューするときは、次を確認します。

  • Function内で、戻り値設定にモジュール名修飾を付けていないか。

  • 関数名と変数名が読み手に混同される形になっていないか。

  • モジュール名修飾が、呼び出し側の名前衝突回避として使われているか。

  • 再帰するFunctionに終了条件があるか。

  • 意図しない自己呼び出しが発生する書き方になっていないか。

第6章 今回のまとめから得られる判断軸

今回の元素材では、標準モジュール名を付けてFunction名へ代入すると再帰が発生する、という不思議な挙動が話題になっていました。

この現象は、次のように整理できます。

  1. Function名は、外側ではプロシージャ名である。

  2. Function内部では、同じ名前が戻り値設定にも使われる。

  3. 関数名だけの代入なら、戻り値設定として読める。

  4. モジュール名修飾を付けると、戻り値用の名前ではなく、モジュール内プロシージャ参照として解釈されやすい。

  5. その結果、左辺評価の過程で同じ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)。