Power Query共通ライブラリの評価環境を設計する

LWP | Power Query共通ライブラリの評価環境を設計する

LWP TECHNICAL ARTICLE | 170

Power Query共通ライブラリの評価環境を設計する

Expression.Evaluate、環境レコード、#shared、TLibを安全につなぐ

Copyright © 2026 LWP 山中 一弘

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

記事要約

Power Queryの共通ライブラリは、関数をレコードにまとめるだけでは完成しません。Expression.Evaluateで動的なM式を実行するなら、その式から見える名前を環境レコードとして明示する必要があります。

本記事では、TLibを名前付きで渡す方式、必要な関数をフラットに渡す方式、#sharedと合成する方式を比較します。公開名の衝突、過剰な権限、デバッグ困難を避けるため、既定は最小環境とし、ライブラリの公開面を契約として管理します。

本記事の対象とゴール

想定読者

  • M関数を複数クエリで共通利用する方

  • TLibのようなレコード型ライブラリを設計する方

  • Expression.Evaluateの名前解決エラーを解消したい方

本記事で得られること

  1. 環境レコードの役割を説明できます。

  2. 名前付き公開とフラット公開を選択できます。

  3. 最小権限の動的評価環境を設計できます。

目次

  • はじめに 評価環境まで含めてライブラリです

はじめに 評価環境まで含めてライブラリです

本記事は、Power Queryで共通ライブラリを構築する際に必要となる考え方と設計上の注意点を、特に Expression.Evaluate と評価環境の扱いを中心に整理したものである。

対象は、次のような構成を考えている場合である。

  • 共通関数群を TLib のようなレコードにまとめる

  • 各クエリや各テーブル定義からその共通関数群を呼び出す

  • テーブル上や設定値上に保存されたMコードを Expression.Evaluate で動的実行したい

  • ライブラリを単なる関数集ではなく、運用可能な共通基盤として設計したい

本記事では、単に Expression.Evaluate の使い方だけでなく、最初からライブラリを構築する際に必要となる次の論点をまとめる。

  • ライブラリとは何か

  • レコードベースのライブラリ設計

  • 環境レコードとは何か

  • #shared とは何か

  • Expression.Evaluate の第2引数はなぜ必要か

  • 動的評価コードにどの名前を見せるべきか

  • 名前空間設計と名前衝突

  • セキュリティ・保守性・運用性の観点

  • 推奨構成と避けるべき構成

2. 背景――なぜ Expression.Evaluate でエラーが出るのか

Power Queryでは、Mコードを文字列として保持し、それを Expression.Evaluate で評価できる。

典型的には次のような構成である。

let
    tbl = ...,
    mlg = tbl{[Name="mcode_01"]}[Item],
    exp = Expression.Evaluate(mlg)
in
    exp

一見これで動きそうに見えるが、mlg の中で TLib、path_join、table_from、env_read などの名前を参照していると、評価時に「名前が見つからない」「参照できない」「環境にない」といったエラーになることがある。

原因は、Expression.Evaluate は、現在のクエリに見えている名前を自動的に全部引き継ぐわけではないからである。

基本形は次のとおり。

Expression.Evaluate(式, 環境レコード)

ここで第2引数を省略すると、期待した名前空間が評価先に渡らず、動的に評価された式の中から TLib や共通関数が見えないことがある。

つまり、ライブラリ設計では 「関数を作る」ことと同じくらい、「どの環境で評価するかを設計する」ことが重要になる。

3. Power Queryにおけるライブラリとは何か

Power Queryでは、実務上のライブラリを レコードに関数をまとめたものとして実装すると扱いやすい。

let
    TLib = [
        path_join = (a as text, b as text) as text => a & "\" & b,
        value_is_blank = (x as any) as logical => x = null or x = "",
        env_read = () as record => [Root="C:\Data"]
    ]
in
    TLib

この TLib はレコードであり、各フィールドに関数が入っている。

呼び出し側では、

let
    path_join = TLib[path_join],
    x = path_join("C:\Data", "abc.xlsx")
in
    x

のように使える。

ライブラリの本質

Power Queryにおけるライブラリは、実務上は次のように考えられる。

  1. 関数を集めたレコード

  2. クエリとして定義された関数群

  3. それらを束ねる親レコード

  4. 補助的に設定テーブルやワークブック参照を持つ仕組み

したがって最初から、

  • どの関数を公開するか

  • どの名前で公開するか

  • TLib[path_join] 形式か path_join() 形式か

  • 動的評価コードにどこまで公開するか

  • #shared とどう組み合わせるか

を決めておく必要がある。

4. 重要用語整理

4.1 レコード

名前付きフィールドの集合。

[
    A = 1,
    B = "abc"
]

4.2 フィールドアクセス

TLib[path_join]

4.3 環境レコード

Expression.Evaluate に渡す第2引数。評価される式から見える名前空間を表す。

[path_join = ..., table_from = ...]

を渡せば、評価式の中から path_join(...) や table_from(...) が見える。

4.4 #shared

Power Queryの共有済み名前空間をレコードとして取得する仕組み。

#shared

標準関数や共有されたクエリ名などを含む。

4.5 Expression.Evaluate

文字列として持っているMコードを評価する関数。

Expression.Evaluate("1 + 2")

実務では通常、第2引数の環境レコードが重要になる。

4.6 共有環境とローカル環境

  • 共有環境:#shared で得られる共有名群

  • ローカル環境:現在の let 内で定義した TLib や path_join など

Expression.Evaluate を使うと、この境界を明示的に扱う必要がある。

5. Expression.Evaluate の基本

5.1 最も単純な形

Expression.Evaluate("1 + 2")

5.2 環境を渡す形

Expression.Evaluate(
    "path_join(""C:\Data"", ""abc.xlsx"")",
    [path_join = path_join]
)

この場合、評価文字列の中で path_join(...) が使える。

5.3 環境を渡さない場合

Expression.Evaluate("path_join(""C:\Data"", ""abc.xlsx"")")

path_join が評価環境に存在しなければエラーになる。

したがって、動的評価は「どの名前を見せるか」を明示設計するものと考えた方がよい。

6. 今回の典型問題

let
    env_read = TLib[env_read],
    value_is_blank = TLib[value_is_blank],
    function_describe = TLib[function_describe],
    function_help = TLib[function_help],
    list_scan = TLib[list_scan],
    list_partition = TLib[list_partition],
    list_pairwise = TLib[list_pairwise],
    path_join = TLib[path_join],
    path_parse = TLib[path_parse],
    columns_prioritize = TLib[columns_prioritize],
    columns_type_convert = TLib[columns_type_convert],
    columns_tail_trim = TLib[columns_tail_trim],
    workbook_from = TLib[workbook_from],
    table_from = TLib[table_from],
    table_lookup = TLib[table_lookup],
    table_error_replace = TLib[table_error_replace],
    table_error_describe = TLib[table_error_describe],
    tbl =
        let
            wkb = workbook_from(),
            tbl = table_from(wkb, [Name="PWQ"])
        in
            tbl,
    mlg = tbl{[Name="mcode_01"]}[Item],
    exp = Expression.Evaluate(mlg)
in
    exp

mlg の中で TLib や path_join などを参照すると、評価時に見えないことがある。

つまり、現在クエリで let に書いた名前が、動的評価式にもそのまま見えるとは限らない。

7. 解決策の基本パターン

7.1 TLib をそのまま渡す方式

TLib が関数名を直接持つレコードなら、

Expression.Evaluate(
    mlg,
    TLib
)

とできる。

評価コード側:

let
    x = path_join("C:\Data", "abc.xlsx")
in
    x

7.2 TLib を名前付きで渡す方式

Expression.Evaluate(
    mlg,
    [TLib = TLib]
)

評価コード側:

let
    x = TLib[path_join]("C:\Data", "abc.xlsx")
in
    x

7.3 #shared と併用する方式

Expression.Evaluate(
    mlg,
    Record.Combine({
        #shared,
        TLib
    })
)

または、

Expression.Evaluate(
    mlg,
    Record.Combine({
        #shared,
        [TLib = TLib]
    })
)

8. #shared の役割

#shared は共有済みの名前を格納したレコードで、標準関数や共有クエリなどが含まれる。

動的評価コード内で標準関数を使いたい場合に便利である。

Expression.Evaluate(
    mlg,
    #shared
)

ただし、#shared を丸ごと渡すと、

  • 名前衝突

  • 想定外のクエリや関数へのアクセス

  • ライブラリ境界の曖昧化

  • 動的実行コードの自由度が高くなりすぎる

といった問題がある。

そのため、試作段階では #shared、本格運用では必要な名前だけに絞るのがよい。

9. Record.Combine による環境合成

複数の環境レコードを合成したい場合に使う。

Record.Combine({
    #shared,
    TLib
})

名前付きでライブラリを渡すなら、

Record.Combine({
    #shared,
    [TLib = TLib]
})

10. ライブラリ設計の2つの流儀

10.1 ライブラリ名を明示する方式

TLib[path_join](...)

長所

  • ライブラリ由来であることが明確

  • 名前衝突しにくい

  • 公開境界が分かりやすい

短所

  • 記述が長くなる

推奨環境:

Expression.Evaluate(
    mlg,
    Record.Combine({
        #shared,
        [TLib = TLib]
    })
)

10.2 ライブラリ関数をフラットに公開する方式

path_join(...)

長所

  • 簡潔

  • 通常の関数呼び出しに近い

短所

  • 名前衝突しやすい

  • 出所が分かりにくい

推奨環境:

Expression.Evaluate(
    mlg,
    Record.Combine({
        #shared,
        TLib
    })
)

10.3 どちらを使うか

小規模ならフラット方式でもよい。

長期運用・複数人開発なら、TLib[...] のようにライブラリ名を明示する方式の方が安全である。

11. 推奨アーキテクチャ

11.1 ライブラリ層

TLib を親レコードとして持つ。

[
    env_read = ...,
    path_join = ...,
    path_parse = ...,
    table_from = ...,
    table_lookup = ...,
    table_error_replace = ...
]

必要ならサブレコード化する。

[
    path = [
        join = ...,
        parse = ...
    ],
    table = [
        from = ...,
        lookup = ...
    ]
]

11.2 利用層

let
    path_join = TLib[path_join],
    x = path_join("C:\Data", "a.xlsx")
in
    x

11.3 動的評価層

実用寄り

Expression.Evaluate(
    mlg,
    Record.Combine({
        #shared,
        [TLib = TLib]
    })
)

簡潔寄り

Expression.Evaluate(
    mlg,
    Record.Combine({
        #shared,
        TLib
    })
)

12. 実務での設計方針

原則1 評価環境は明示する

Expression.Evaluate(mlg) のように第2引数なしで済ませない。

原則2 #shared 依存は必要最小限にする

便利だが、公開範囲が広すぎる。

原則3 ライブラリ公開名を整理する

例:

  • path_join

  • path_parse

  • table_from

  • table_lookup

  • columns_prioritize

カテゴリ接頭辞を付けると衝突しにくい。

原則4 動的評価コードの責任範囲を限定する

  • 呼び出せる関数

  • 参照できるデータ

  • 入出力の期待型

を決めておく。

原則5 ライブラリを「契約」として設計する

各関数について、

  • 引数

  • 返り値

  • null の扱い

  • エラー時の動作

  • 前提環境

  • 相互依存関係

を定義する。

13. Expression.Evaluate 利用時のリスク

13.1 セキュリティ

文字列として保持されたMコードを評価する以上、実質的にコード実行機構である。

13.2 保守性

  • 実行コードの追跡が難しい

  • 静的検索しにくい

  • 依存関係が分かりにくい

13.3 デバッグ

  • エラー位置が分かりにくい

  • 型情報が見えにくい

13.4 対策

  • 評価文字列をログ化

  • 環境レコードを最小化

  • 実行コードをテンプレート化

  • 完全自由記述にしない

  • function_describe、function_help などのメタ情報関数を整備する

14. 具体的な改善例

現状

exp = Expression.Evaluate(mlg)

まず動かす

exp =
    Expression.Evaluate(
        mlg,
        Record.Combine({
            #shared,
            [TLib = TLib]
        })
    )

フラット公開

exp =
    Expression.Evaluate(
        mlg,
        Record.Combine({
            #shared,
            TLib
        })
    )

15. ライブラリ構築時の推奨手順

  1. ライブラリの親レコード TLib を作る

  2. 関数の命名規則を決める

  3. TLib[path_join] 方式かフラット方式か決める

  4. Expression.Evaluate 用の環境設計を決める

  5. #shared の利用範囲を決める

  6. function_describe、function_help などを整備する

  7. 引数・返り値・エラー条件などの契約を文書化する

  8. 動的評価コードをテンプレート化する

16. よくある設計ミス

ミス1 Expression.Evaluate に環境を渡さない

最も典型的。

ミス2 #shared だけで全部済ませる

ライブラリ境界が曖昧になる。

ミス3 独自関数名と標準関数名が衝突する

命名規則で避ける。

ミス4 動的コードに自由度を与えすぎる

運用・保守が崩れやすい。

ミス5 ライブラリ内の依存関係が見えない

関数同士の依存も管理する。

17. 実務上の推奨結論

小規模・試作

Expression.Evaluate(
    mlg,
    Record.Combine({
        #shared,
        [TLib = TLib]
    })
)

本格運用

必要なものだけを公開する評価環境へ寄せる。

let
    EvalEnv = [
        TLib = TLib,
        path_join = TLib[path_join],
        path_parse = TLib[path_parse],
        table_from = TLib[table_from],
        table_lookup = TLib[table_lookup],
        table_error_replace = TLib[table_error_replace],
        TextTrim = Text.Trim,
        ListTransform = List.Transform
    ],
    exp = Expression.Evaluate(mlg, EvalEnv)
in
    exp

18. まとめ

Power Queryで共通ライブラリを構築するうえで重要なのは、単に関数を作ることではない。

どの名前を、どの環境で、どこまで公開して評価するかという名前空間設計が本質である。

ポイントは次のとおり。

  1. Expression.Evaluate は第2引数の環境レコードが重要

  2. TLib を使うなら動的評価側にも明示的に渡す

  3. 標準関数を使うなら #shared が役立つ

  4. #shared は便利だが公開範囲が広い

  5. Record.Combine で共有環境と独自環境を合成できる

  6. 長期運用では必要最小限の名前だけを公開する

  7. Power Queryライブラリは「公開インターフェースと評価環境を持った共通基盤」として設計する

19. 推奨の最終形

let
    EvalEnv =
        Record.Combine({
            #shared,
            [TLib = TLib]
        }),
    tbl =
        let
            wkb = TLib[workbook_from](),
            tbl = TLib[table_from](wkb, [Name = "PWQ"])
        in
            tbl,
    mlg = tbl{[Name = "mcode_01"]}[Item],
    exp = Expression.Evaluate(mlg, EvalEnv)
in
    exp

mcode_01 側:

let
    x = TLib[path_join]("C:\Data", "abc.xlsx"),
    y = Text.Trim(" abc ")
in
    x

フラット公開するなら、

let
    EvalEnv =
        Record.Combine({
            #shared,
            TLib
        }),
    exp = Expression.Evaluate(mlg, EvalEnv)
in
    exp

mcode_01 側:

let
    x = path_join("C:\Data", "abc.xlsx")
in
    x

20. 最後に

Power Queryのライブラリ設計は、一見すると「関数をまとめるだけ」に見える。

しかし動的評価を使い始めると、本質は 環境設計・公開設計・依存関係設計 にある。

したがって、最初から次の順序で考えるとよい。

  1. 関数を作る

  2. ライブラリレコードにまとめる

  3. 公開名を決める

  4. Expression.Evaluate 用の環境レコードを決める

  5. #shared の使い方を決める

  6. 動的実行コードの境界を決める

  7. ドキュメント化する

これを押さえると、Power Queryでもかなりしっかりした共通ライブラリ基盤を構築できる。

出典メモ

  • Microsoft Learn『Expression.Evaluate』 https://learn.microsoft.com/en-us/powerquery-m/expression-evaluate

  • Microsoft Learn『Basic concepts』 https://learn.microsoft.com/en-us/powerquery-m/m-spec-basic-concepts

  • Microsoft Learn『Sections』 https://learn.microsoft.com/en-us/powerquery-m/m-spec-sections

  • TakiLib Power Query正本 taki3lib4PQ.txt(確認時バージョン1.0.40)