📑

verse言語の設計思想を読み解きたい(3)失敗コンテキスト:①if式の述語部

2023/04/05に公開

前回はこちら
https://zenn.dev/t_tutiya/articles/e0f5cf08be3708

前回、失敗コンテキストについて概念的な部分をまとめてみたのですが、自分で書いていてもよくわからない感じになってしまいました。
そこで、改めて失敗コンテキストについて勉強することにしました。何回かに分ける予定で、今回はif式編です。

注意:「failable」の訳語について(★このブログだけの話★)

前回「失敗する可能性のある(failable)」という訳語について色々書きましたが、このブログでは、以降"failable"の訳語を「失敗許容(failable)」にしようと思っています[1]。公式訳にはない用語なので、適宜読み替えて頂ければと思います。

公式訳との対応は以下の様になります。

原文 公式訳 このブログ
failable operator 失敗する可能性がある演算子 失敗許容演算子
failable expression 失敗する可能性がある式 失敗許容式
failable function 失敗する可能性がある関数 失敗許容関数

とはいえ、やってみてイマイチだったらサクっと変えちゃいますんでその辺ご容赦ください。

6種類の失敗コンテキスト

https://dev.epicgames.com/documentation/ja-jp/uefn/failure-in-verse

verseにおいて、以下の6箇所が失敗コンテキストとなります[2]

①if式内の述語部
②for式内のジェネレーター式とフィルター式
③decides指定のある関数(メソッド)のボディ
④not演算子のオペランド
⑤or演算子の左オペランド
⑥オプション型の初期化部

失敗コンテキスト内にのみ、「失敗許容式(failable expression)」が記述できます。以下、この6種類の失敗コンテキストを順に見て行きます。

①if式の述語部

https://dev.epicgames.com/documentation/ja-jp/uefn/if-in-verse
verseではif式の括弧内の事を「述語部(preducate)[3]」と呼び、then/else分岐の条件を表す式を記述できます[4]。verseではC#と異なり、述語部に記述する式は真偽値(verseではlogic型)を返す必要がありません。また、述語部に複数の式を記述できるのも特徴です。

実際に公式ドキュメントのサンプルコードからif式の挙動を確認してみます。

if (Player := player[Agent], PlayerUI := GetPlayerUI[Player]):
    MyUI : text_block = text_block{DefaultText := TextForMyUI} #text_blockクラスのインスタンスMyUIを定数定義(解説省略)
    PlayerUI.AddWidget(MyUI)

このコードでは、述語部で以下の二つの式を実行しています。

Player := player[Agent] #①
PlayerUI := GetPlayerUI[Player] #②

":="は変数・定数初期化演算子で、①ではPlayer定数、②ではPlayerUI定数を定義しています。

①②のどちらの式も配列アクセスのように見えますが、実際には①はダウンキャストで(!)、②は関数呼び出しです(!!)。もちろん添え字での配列アクセスもありまして、これらは見た目上の区別はつきません。

失敗許容関数を呼び出す場合は、②の式のように引数を囲む括弧を"()"ではなく"[]"にしなければいけないというルールがあります。これは想像ですが「"["~"]"が付与された式は全て失敗許容式である」というコード上の一貫性を持たせているのかもしれません(実際、配列アクセスもダウンキャストも失敗許容式です)。

述語部の評価には「式①が成功、かつ、式②が成功」「式①が失敗」「式①が成功、かつ式②が失敗」の3種類が有り得ます。それぞれについて見て行きます。

成功パターン:式①が成功、かつ、式②が成功

まず式①が評価されます。Agentオブジェクトがplayerクラスにダウンキャスト出来ればPlayer定数がその結果によって初期化されます。これを「成功」したと言います。

式①が成功した場合、続いて式②が評価されます。述語部では前の式で定義された値を参照できます。ここではGetPlayerUIメソッドの引数にPlayer定数が指定されています。

式②も成功した場合、述語部内の全ての式が成功したことになり、then節が実行されます。述語部で定義された値は、then節内でも参照できます。ここでは述語部で定義されたPlayerUI定数を介してAddWidget()メソッドを実行しています。

失敗パターンA:式①が失敗

式①が評価された際に、Agentインスタンスがplayer以外のクラスだった為にダウンキャスト出来ない場合[5]、その式は「失敗」したと言います。

式①が失敗した場合、条件判定はその時点で終了します(式②は実行されません)。この時、「述語部全体が実行されなかった」とされます。つまり、Player/PlayerUIいずれの定数も定義されません。

ifの述語部が失敗した時、else節があればそちらが実行されます。Player/PlayerUIのスコープは述語部とthen節だけなので、else節からは参照できません。というか、述語部全体が実行されなかったとされるので、これらの定数はそもそも定義されていないのです[6]

失敗パターンB:式①が成功、かつ式②が失敗

式①が成功した後で式②が失敗した場合も、やはり条件判定は終了しします。失敗パターンAの場合と同じくelse節があればそちらが実行されます。この時Player/PlayerUIは参照できません。

注意したいのは、述語部が失敗した場合、既に述語部で成功した式があったとしても「述語部全体が実行されなかった」とされるという点です。これはつまり、もし式①で副作用のある処理が行われていたとしても、その処理はロールバックされる事を意味します[7]

このように、verseには言語レベルでトランザクション処理が組み込まれているのです(トランザクション処理についてはまたいずれ)。

#fortnite #uefn #verse #verselang

続き

https://zenn.dev/t_tutiya/articles/bc1641e4de71b9
なおif式は他にも機能が沢山あるので、単体で別で取り上げる予定です。


verse言語とUEFNの記事を他にも書いているので御覧下さい。
https://zenn.dev/t_tutiya

最後まで読んで頂きありがとうございました。この記事がお役に立てたようであれば、是非LIKEとフォローをお願いします(今後の執筆のモチベーションに繋がります)。

#Verse #UEFN #Fortnite #Verselang #UnrealEngine

脚注
  1. C#でnullableを「null許容」と訳すのに準じました ↩︎

  2. 公式ドキュメントと記述内容に差異があります ↩︎

  3. 公式訳では「述語」 ↩︎

  4. 括弧を使わない記法もあります ↩︎

  5. agent派生クラスにはplayerクラス、AIクラスの2種類がある ↩︎

  6. 「定義されなかったから参照出来ない」のか「スコープから外れているから参照出来ない」のかはドキュメントを見ても良く分からなかった ↩︎

  7. まだちゃんと勉強していないので、自分で書いていながら「ホントにそんな事出来るの……?」と思ってはいる ↩︎

Discussion