IfError から Notify を削ろうとしたら構文エラーになる
ストアドプロシージャの実行を IfError で受け取り、エラー時に Notify を表示していました。
また、呼び出しと値の受け取りが問題なければデータ処理を実行しています。
IfError(
// ストアドプロシージャ呼び出し → 結果を変数に格納
UpdateContext({
locSaveDataSpResults: First(
sqlConnector.dboSaveDataSp({
UserId: gblUserId,
SaveData: gblSaveDataText,
...
}).ResultSets.Table1
)
});
// 戻り値をチェック
If(
locSaveDataSpResults.Result = 1,
// 成功時の処理...
),
// エラーが発生した場合
Notify("処理中にエラーが発生しました", NotificationType.Error)
)
ある時、この Notify が冗長だと気づきました。ストアドプロシージャ側でシステムエラーが発生した場合、Power Apps は自動的にエラーメッセージを表示します。アプリ側でさらに「処理中にエラーが発生しました」と重ねて通知しても、ユーザーに具体的な対処を案内して復帰できるわけでもない。
そこでシンプルに Notify(...) を削除しようとしました。
IfError(
UpdateContext({
locSaveDataSpResults: First(
sqlConnector.dboSaveDataSp({
UserId: gblUserId,
SaveData: gblSaveDataText,
...
}).ResultSets.Table1
)
});
If(
locSaveDataSpResults.Result = 1,
// 成功時の処理...
)
// ← エラー時: 何もしない
)
構文エラーになりました。
IfError は第2引数が必須です。コメントだけ書いても引数として認識されません。省略という選択肢がない。
では「何もしない」を明示的に書くにはどうすればいいのか? AIに聞いたところ、Blank() を使うと教えてもらいました。
IfError(
UpdateContext({
locSaveDataSpResults: First(
sqlConnector.dboSaveDataSp({
UserId: gblUserId,
SaveData: gblSaveDataText,
...
}).ResultSets.Table1
)
});
If(
locSaveDataSpResults.Result = 1,
// 成功時の処理...
),
Blank() // エラー時: 何もしない(空の値を返す)
)
動きました。しかしなぜ Blank() で「何もしない」になるのか?
Notify(...) なら「通知を出す」という処理なので、書くことに違和感がない。でも Blank() そのものは「空」。これが「何もしない処理」として機能するのか?
Ifの引数がずれるバグから気づいたこと
この疑問がもっと切実になったのは、あるバグに遭遇したときでした。
保存ボタンのOnSelectにこんなコードがあったとします。
If(
// 変更データがない場合
CountRows(colChangeData) = 0,
Notify("保存対象のデータがありません", NotificationType.Error),
// ユーザーが特定されていない場合
IsBlank(gblUserId),
//Notify("ユーザIDが設定されていません", NotificationType.Error),
// バリデーションOK → 保存処理に進む
With({...}, ...)
)
2つ目の Notify(...) をコメントアウトしているだけに見えますが、実はこれ、意図通りに動いていませんでした。
PowerFxの If は If(条件1, 結果1, 条件2, 結果2, ..., デフォルト) という形式です。結果部分をコメントアウトすると、引数の位置がずれます。
実際にパースされるとこうなります。
If(
CountRows(colChangeData) = 0, // ← 条件1
Notify("保存対象のデータがありません", ...), // ← 結果1
IsBlank(gblUserId), // ← 条件2(位置は合っている)
With({...}, ...) // ← 結果2(!?)
// 本来はデフォルトのはず
)
// → gblUserIdがBlankのとき With が実行される(保存されてしまう)
// → 両条件がfalseの正常ケースでは Blank() が返り、保存されない
IsBlank(gblUserId) の位置はずれていないように見えますが、本来「デフォルト(全条件がfalseのとき)」のはずだった With({...}) が「条件2がtrueのときの結果」として解釈されてしまいます。意図と真逆の動きになります。
修正はこうです。
If(
CountRows(colChangeData) = 0,
Notify("保存対象のデータがありません", NotificationType.Error),
IsBlank(gblUserId),
Blank(), // ← Blank() で枠を維持
//Notify("ユーザIDが設定されていません", NotificationType.Error),
With({...}, ...) // ← デフォルト: 保存処理(意図通り)
)
Blank() を書くことで引数の位置が正しくなり、意図通りに動くようになりました。
でも……なぜこうなるのか? JavaScriptのように if (...) { } と空ブロックにすれば済む話ではないのか?
AIと深く理解してみる
この疑問をClaudeに投げてみたところ、「PowerFxの If や Switch は、他言語のような制御構文(statement)ではなく、関数(expression)です」 と。
関数だから、各引数の位置には「値を返す式」が必要になる。Blank() は「何もしない処理」ではなく「空の値を返す式」で、引数の枠を埋めるために書いている、という説明でした。
なるほど。作法はわかりました。でも、「文と式の違い」って?「宣言的」「関数型」ってどういうことだ? 改めて自分で調べることにしました。
公式ドキュメントを見にいく
まず Microsoft Power Fx overview を読みました。
冒頭のあたりに、PowerFxは 「宣言型」 、そして 「関数型」 のプログラミング言語であるという記述があります。
正直、これだけ読んでも「だから何?」という感じです。でもこの2つの言葉が、掘り下げていくと全部つながってきます。
「宣言的」で「関数型」って、どういうこと?
一番わかりやすいのは、Excelとの比較です。
Excelのセルに =IF(A1>0, "正", "負") と書くとき、「A1が0より大きければ"正"を実行しろ」とは考えません。「A1が0より大きければ、このセルの値は"正"になる」と考えます。
これが 宣言的 ということです。「何をするか」ではなく「どういう状態であるか」を記述する。
そして =IF(...) は関数です。引数を受け取って、値を返す。「上から順に実行する手続き」ではなく、「入力に対して出力を返す関数の組み合わせ」でプログラムが構成される。これが 関数型 の発想ということです。
PowerFxはこのExcelの設計をそのまま引き継いでいます。だから If も Switch も関数であり、必ず値を返します。
「制御構文」と「関数」の違い
ここがこの記事のポイントです。
JavaScriptの if は 制御構文(statement) です。「条件に応じて、処理の流れを分岐させる」ための命令であり、それ自体は値を返しません。
// JavaScript: if文は値を返さない
if (count === 0) {
showError(); // ← 処理を実行する
}
// if文そのものを変数に入れることはできない
一方、PowerFxの If は 関数(expression) です。引数を受け取り、条件に応じた値を返します。
// PowerFx: If関数は値を返す
If(
CountRows(colChangeData) = 0,
false, // ← If() の戻り値が false になる
With({...}, ...) // ← If() の戻り値が With() の結果になる
)
関数なので、各引数の位置には「値を返す式」が入る必要があります。だからこそ、「何もしない」ケースでも Blank() や false で枠を埋めないと、後ろの引数の位置がずれてしまうわけです。
最初に遭遇したバグの原因は、まさにここでした。
戻り値と副作用
関数は値を返す。これはJavaScriptを書いたことがあるので知ってます。でも Notify(...) を書いたときは通知が画面に出ます。「値を返す」だけじゃなく「何かが起きている」。この違いは何でしょうか。
プログラミングでは、関数が「値を返す」以外に外の世界に影響を与えることを 副作用(side effect) と呼びます。
Notify(...) は副作用のある関数です。値を返しつつ、画面にも通知を出す。だから「処理をしている」という実感があります。
一方、Blank() や false は副作用がゼロです。値を返すだけで、画面にも変数にも何も影響しない。だから「何もしていない」と感じる。
でもPowerFxの視点では、どちらも同じ「式として値を返している」という行為です。If 関数の引数の枠に入っている、という点で Notify(...) も Blank() も同等の存在です。
Blank() が「何もしない」と感じるのは自然な感覚ですが、正確には「副作用のない値を返している」ということですね。
false と Blank() の使い分け
ちなみに、実用上の違いはほぼありません。ただし:
-
Blank()→ 「ここは意図的に何もしない」という意思表示として読みやすい -
false→ If/Switchの戻り値を後続で使う場合(バリデーション結果として返すなど)に意味が出る
コードの可読性を考えると、「何もしない」を表現するには Blank() の方が意図が伝わりやすいと思います。
フォールスルーって何?
最後にもう1つ。JavaScriptの switch 文を使ったことがある方は聞いたことがあるかもしれません。フォールスルー(fall-through) という挙動です。
// JavaScript: break を忘れるとフォールスルーする
switch (action) {
case "init":
doInit();
// break を書き忘れた!
case "applyFilter":
applyFilter(); // ← "init" のときもここが実行されてしまう
break;
default:
break;
}
JavaScriptの switch は制御構文なので、case に一致した地点から下に向かって処理が流れ落ちていきます(fall-through)。break を書いて明示的に止めないと、次のケースも実行されてしまう。これは有名なバグの温床です。
PowerFxの Switch にはこの問題がありません。
// PowerFx: 引数のペアで構造が決まる
Switch(
locNavigationContext.action,
"init", UpdateContext({ locExeFlags: { InitUI: true, LoadData: true } }),
"applyFilter", UpdateContext({ locExeFlags: { InitUI: false, LoadData: true } }),
Blank()
)
関数なので、引数の位置で「条件」と「結果」のペアが構造的に決まっています。「条件に一致したら、対応する結果の式を評価して値を返す」という動きしかしない。フォールスルーという概念がそもそも存在しません。
break の書き忘れで意図しないケースが実行される、ということが言語レベルで起きない。これはPowerFxの Switch が関数であることのメリットの1つです。
ここまでを整理する
| JavaScript | PowerFx | |
|---|---|---|
| If / Switch の性質 | 文(statement) | 関数(expression) |
| 「何もしない」の表現 | 空ブロック {} or 書かない |
Blank() で戻り値の枠を埋める |
| フォールスルーの罠 | あり(break 忘れ) |
原理的に起きない |
false; の意味 |
副作用のない式文(linter警告) | 「戻り値がfalse」という有効な式 |
公式ドキュメントへのリンク
この記事で触れた言語設計については、Microsoft公式ドキュメントでも解説されています。
- Microsoft Power Fx overview — PowerFxが「汎用、厳密な型指定、宣言型、そして関数型」であるという設計思想
- Power Fx expression grammar — 式の文法仕様。セミコロンが「チェーンセパレーター」として定義されている
おわりに
「IfError の第2引数を消したら構文エラーになった。じゃあ Blank() でいいのか? でもなぜ Blank() が『何もしない』になるんだ?」という疑問が出発点でした。
AIに聞いたら「IfError は関数だから」と返ってきて、そこから「宣言的とは」「制御構文と関数の違いとは」「戻り値と副作用とは」「フォールスルーとは」を1つずつ調べていったら、PowerFxという言語の設計思想が少しずつ見えてきました。
PowerFxはExcel由来の式ベース言語で、If も Switch も関数。だから引数の枠を埋める必要があるし、フォールスルーのような「知らないと踏む地雷」が言語レベルで排除されている。
他の言語(JavaScriptなど)を少しだけかじった私がPowerFxに入り、制御構文のつもりで書いて引数のずれに気づかない、ということが起きました。逆にプログラミング未経験でPowerAppsから入った方は、そもそもこの違いを意識する機会がない。
どちらの入口から来ても、「PowerFxのIf/Switchは関数である」 を一度理解しておくと、設計判断の見通しがずっと良くなると感じました。
PowerApps 開発メモ シリーズ
- #1: PowerFxの「何もしない」はなぜ Blank() なのか?(本記事)