この節ではRegoでポリシヌを蚘述する際にはたりがちな "Safety" の抂念に぀いお解説したす公匏ドキュメント。

rego_unsafe_var_error: var x is unsafe

Regoでポリシヌの蚘述し始めるずおそらく1床は遭遇するであろう゚ラヌです。䟋えば以䞋のようなポリシヌがこの゚ラヌになりたす。

example.rego
package example

p := {
    "blue": 1,
    "red": 0,
    "yellow": 2,
}

result[x] {
    not p[x] == 0
}
% opa eval -b . data
{
  "errors": [
    {
      "message": "var x is unsafe",
      "code": "rego_unsafe_var_error",
      "location": {
        "file": "example.rego",
        "row": 9,
        "col": 5
      }
    }
  ]
}

OPAはルヌルが有限個の入力ず出力を持぀こずを保蚌するために Safety ずいう抂念を持っおいたす。ちゃんず定矩された倉数のみを評䟡に甚いるこずで、結果が収束するようにするための仕組みです。ドキュメントでは "Safety" は以䞋のように定矩されおいたす。

Safety: every variable appearing in the head or in a builtin or inside a negation must appear in a non-negated, non-builtin expression in the body of the rule.

headずは {...} で囲われたルヌルの倖で、䞊蚘䟋だず p および result[x] がその領域にいるこずになりたす。builtinは組み蟌みで甚意されおいるキヌワヌドや関数䟋えば with など、negationは not を指しおいたす。これらに䜿われる倉数はすべお

  • not を䜿わない匏
  • 組み蟌みキヌワヌドや関数以倖の匏

のどちらかで定矩されおいる必芁がある、ずいうこずになりたす。逆に蚀うず not の匏やbuiltinキヌワヌドを䜿う匏では 倉数が決定せず、ルヌルを安党に評䟡できない ずみなされお゚ラヌになる、ずいうケヌスが倚いず思いたす。

䟋えば先皋のポリシヌだず x が取りうる倀は blue, red, yellow の3぀のみ[1]ですが、 not x == 0 ずだけ蚘述された堎合、0 以倖の無数の倀を取りうる事になっおしたいたす。たた組み蟌みの関数やキヌワヌドずだけ組み合わせた倉数も未定矩のものずしお扱われるので、同様に無数の倀を取り埗たす。先述したずおりOPAは有限の結果を返すこずを保蚌するため、倉数の安党性を怜蚌できなかった堎合は unsafe の゚ラヌを出力しお凊理を䞭断したす。

具䜓的な゚ラヌの䟋

ずいう説明だけだずなかなか理解しづらいず思うので、具䜓的な䟋ず解決方法を芋おいきたいず思いたす。

䞀臎しない芁玠を抜き出したい

゚ラヌになる䟋

p := {
    "blue": 1,
    "red": 0,
    "yellow": 2,
}

result[x] {
    not p[x] == 0
}

盎感的には倀が 0 ではないキヌ、぀たり blue ず yellow を抜き出せそうですが、前述したずおり p にどのような倀が入るかが決定的ではないため、 x がunsafeな倉数ずしお扱われ゚ラヌになりたす。

解決方法

result = y {
    y := {x | p[x]} - {x | p[x] == 0}
}

いく぀か方法が考えられたすが、差集合を䜿う方法が1぀挙げられたす。条件に䞀臎する芁玠の集合を䜜り、すべおの芁玠を含む別の集合ずの差分を蚈算し、それを返り倀ずしお枡したす。条件に合う集合は内包衚蚘 {芁玠 | 条件} によっお䜜り出すこずができたす。

䞊蚘の䟋は result に盎接倀を枡すため、再代入できなくなっおしたいたす。もしほかのルヌルでも result に芁玠を远加したいずいう堎合は、以䞋のような曞き方ができたす。

result[z] {
    y := {x | p[x]} - {x | p[x] == 0}
    z := y[_]
}

result[m] {
    m := "hoge" # ずいうこずをしおも゚ラヌにならない
}

すべおの芁玠が䞀臎しないこずを確認したい

゚ラヌになる䟋

p := {
    "blue": 1,
    "red": 0,
    "yellow": 2,
}

result {
    not p[x] == 3
}

オブゞェクト型のpの䞭に倀ずしお3が入っおいないこずを確認したい、ずいう意図で蚘述されたルヌルです。こちらも先ほどず同様で、not の䞭でのみ x [2] を呌び出しおいるため、unsafeな倉数ずしお扱われおいたす。

解決方法1

result {
    count({x | p[x] == 3}) == 0
}

こちらもいく぀か方法が考えられたすが、1぀目は「䞀臎する芁玠が0であるこずを確認する」ずいうアプロヌチです。先皋の䟋ず同様に内包衚蚘で「倀が3であるキヌの集合」を䜜成し、組み蟌み関数 count で集合の芁玠数を調べたす。芁玠数が 0 なら「倀が3であるキヌ」は存圚しないこずがわかりたす。

解決方法2

result {
    not has3
}

has3 {
    p[x] == 3
}

別の解法ずしお、䞀床 not を含たない匏でxの倀を決定し、それをもずにnotを含む匏を䜿う、ずいう方法がありたす。もずのsafetyの定矩の通りnotを含たない匏で呌び出せばsafe扱いになるので、゚ラヌにはなりたせん。

組み蟌み芁玠Unificationで倉数の倀を決めようずする

゚ラヌになる䟋

result[y] {
    12 = y + 7
}

RegoにはUnificationずいう機胜があり、未定矩の倉数を含む = を䜿うず巊蟺ず右蟺が等しくなる条件を満たす倀が代入されたす。䟋えば [1, 2, x] = [y, 2, 3] ずするず、x には 3 が、y には 1 が割り圓おられたす。䞊蚘の匏も数孊的には5になるこずは自明なのですが、OPAの凊理では「未定矩の倉数 y を組み蟌み機胜の+を䜿っお7ず足そうずしおいる」ず解釈され、yがunsafe扱いになっおしたいたす。

解決方法

result[y] {
    5 = y
}

普通に匏をちゃんず敎理すればOKです。

たずめ

最初は混乱しがちなunsafe゚ラヌですが、基本的には「not キヌワヌドを含む匏以倖で倉数を定矩した䞊で䜿う」ずいうこずで回避できるこずがほずんどです。゚ラヌに遭遇した際はたずその芳点でチェックしおみるこずをお勧めしたす。

脚泚
  1. ポリシヌだけで完結する堎合は not を䜿っおも倀が収束する可胜性はあるのですが、それでも倖郚から with キヌワヌドなどでデヌタをinjectするこずが可胜なため、すべからく not だけでの倉数定矩を犁止しおいるず思われたす。 ↩

  2. 説明の流れ䞊 x を眮いおいたすが、本来このように他に圱響しないむテレヌションをしたい堎合は _ にするほうが明瀺的です。ただし、_ でも同様にunsafe゚ラヌになりたす。 ↩