0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【PowerQuery】「すべてが関数」の世界で、最後に私がバナナとリンゴを渡す理由

0
Last updated at Posted at 2025-12-13

自分の頭の中のプログラミングパラダイムは、次の2つで占められています。

  • 手続き型: データ(構造体)と処理(関数)は明確に分離される。
  • オブジェクト指向(OOP): データ処理をクラスという単位でカプセル化(統合)し、一体モノとして扱う。

OOPで一体モノとして扱っていますが、頭の中では別のモノとして認識しています。

ところが、最近、PowerQuery(M言語)を通じて「ラムダ計算」の世界に触れ、そこにはさらなる抽象化の段階があり、

  • 「データも処理も区別なく「すべて『関数』」として扱う」 というパラダイムでした。

PowerQuery(M言語)で True や False といった真偽値そのものを関数で表現したり、NOT関数を実装していくなかで、この「すべてが関数である世界」の特殊さと、そこで見つけた 「純粋なロジックと具体的な値(バナナとリンゴ)の関係性」 が、手続き型やOOPに慣れ親しんだ頭にカツンと来たので覚書しています。

1. 衝撃、データすらも「関数」である

ラムダ計算(チャーチ・エンコーディング)のアプローチでは、 True や False といった真偽値すらも、静的なデータではないということ。衝撃でした。チャーチ・エンコーディングでは、これらは「2つの選択肢からどちらかを選ぶ」という機能を持った関数として定義します。

// MyTrue: 2つの引数を受け取り、1つ目(x)を返す関数
MyTrue  = (x, y) => x,
// MyFalse: 2つの引数を受け取り、2つ目(y)を返す関数
MyFalse = (x, y) => y,

手続き型やOOP脳の私の頭は 「フラグ(データ)を見て分岐する」 という考えをしますが、ラムダ計算では、 「フラグ自体が分岐させる機能を持っている」 イメージです。

さらに、論理否定 NOT 関数を作ったのですが、これはかなり難解でして、自力では作れず、生成AIを頼りに作りました。

出来上がったコードを後から見て言語化してみると、 「True(1つ目の引数を返す人)」が来たら、「False(2つ目の引数を返す人)」に変身させる イメージなのかなぁといった感じです。

// MyNot: 真偽値関数(bool_func)を受け取り、その適用順序を入れ替える
MyNot   = (bool_func) => (x, y) => bool_func(y, x),

ここで MyNot(MyTrue) を実行しても、まだ具体的な値(文字列や数値)は一切登場しないのです。返ってくるのは MyFalse と等価な 「関数(振る舞い)」 だけです。
めちゃくちゃ抽象の世界です。

2. 「論理の構築」と「値の適用」

ここまでできて「面白い」と思ったのは、 「論理構造の構築」と「具体的な値の適用」 が完全にフェーズ分けされているという点です。

以下のPowerQuery(M言語)のコードは、 NOT 関数をテストしているコードですが、前半部分の論理構築フェーズでは、純粋に関数だけを操作し、後半でやっと現実世界の値を持ち込んでいます。

let
    // ==========================================
    // 1. 論理構築フェーズ
    // ここには具体的なデータは存在しない。純粋な関数の組み合わせのみ。
    // ==========================================
    // MyTrue: 2つの引数を受け取り、1つ目(x)を返す関数
    MyTrue  = (x, y) => x,
    // MyFalse: 2つの引数を受け取り、2つ目(y)を返す関数
    MyFalse = (x, y) => y,
    // MyNot: 真偽値関数(bool_func)を受け取り、その適用順序を入れ替える
    MyNot   = (bool_func) => (x, y) => bool_func(y, x),

    // 「反転させる」というロジックを定義
    // Logic_Inverted は、「2つの選択肢が来たら後ろを選ぶ」という性質を持つ関数となる
    Logic_Inverted = MyNot(MyTrue),
    

    // ==========================================
    // 2. 値の適用(注入)フェーズ
    // 構築された抽象的な構造に、具体的な値を適用(流し込んで)結果を得る。
    // ==========================================

    // パターンA: 文字列という「具体的な世界」を適用(注入)する
    // -> 仕組みに従い "リンゴ" が選ばれる
    Result_String = Logic_Inverted("バナナ", "リンゴ"),

    // パターンB: 数値という「具体的な世界」を適用(注入)する
    // -> 仕組みに従い 0 が選ばれる
    Result_Number = Logic_Inverted(100, 0),

    // 結果をリストに変換
    Result ={ Result_String, Result_Number }
in
    Result

こんな結果が返ります。Trueの場合は2つの引数の1つ目が返り、Falseは2つ目の引数が返るので、想定通りの挙動となっています。
image.png

3. なぜ最後にバナナとリンゴを渡すのか?

NOT 関数の作成を通して、自身の最大の気づきは 「関数(ロジック)」と「値(コンテキスト)」の完全な分離 です。

  • Logic_Inverted(関数): ここにあるのは「2つの引数の内、後ろの人を選ぶ」という純粋な構造だけです。型もなければ値もないです。
  • ("バナナ", "リンゴ") (値): ここで初めて、その構造に対して、人間が解釈可能な実体を与えます。

OOPではインターフェースを使ってポリモーフィズムを実現しますが、ラムダ計算では全てが関数で表現され、とにかく抽象度が高いので、インターフェースのような特別な仕掛け無しに 「文字列でも数値でも、構造さえ合えば何でも受け入れる」 という柔軟性を発揮する点が衝撃的でした。

「関数」は純粋なルールの世界に住んでいて、「値」はそれを現実世界に具現化するために「最後」に渡される

そんな感覚でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?