Prologはパラドックスの夢を見るか？

本記事は[エムティーアイ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/mti)の10日目の記事です。

※タイトルと内容はあまり関連していません。
※Prologを初めて触りました、程度の記事です。

## はじめに
先日、ロボットがパラドックスを考えて壊れてしまう描写を観てからというもの、
Prologならどうなるのか（またどう解決するのか）が気になり夜しか眠れません。
ただ、「Prolog パラドックス」と調べてもあまり記事は出てこなかったため、実際に試してみました。

## 書いてみる
### 文法
Prologを初めて触るため、文法を確認します。

```prolog
a(b). % b = a
a(b, c). % bとcは、aという関係を持つ
a :- b. % bならば、a
a :- b, c % bかつcならば、a
```

### 自己言及のパラドックスを書く
試しに「私は嘘つきです」という自己言及のパラドックスを与えてみます。
上記のパラドックスをProlog内でどう表現すべきか分からなかったのですが、以下のように記述してみました。

```prolog
% "lierが何かを言う"場合の規則を以下のように捉える
% not(hoge) :- is_lier(X), say(X, hoge).
% hoge :- not(is_lier(X)), say(X, hoge).

not(is_lier(X)) :- is_lier(X), say(X, is_lier(X)). 
% Xがlierである、かつ、Xが（Xはlierである）と言うならば、Xが話す内容（Xはlierである）は偽である
is_lier(X) :- not(is_lier(X)), say(X, is_lier(X)). 
% Xがlierでない、かつ、Xが（Xはlierである）と言うならば、Xが話す内容（Xはlierである）は真である
```

実行結果
案の定パラドックスに陥り、何も結果を返してくれません。
![スクリーンショット 2021-12-02 004741.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271893/97384fcf-1aab-c5b3-3bb7-60682bf5cc4f.png)

実行過程のトレース
嘘つきか嘘つきでないかを延々とループしてしまっているようです。
![スクリーンショット 2021-12-02 231113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271893/f65e965c-f4b4-0544-d8c7-04493b2465c2.png)

### パラドックスを回避する
いくらプログラムと言えど延々とループさせるのは酷なため、なんとか回避させてあげたいです。
[Wikipedia 自己言及のパラドックスのページ](https://ja.wikipedia.org/wiki/%E8%87%AA%E5%B7%B1%E8%A8%80%E5%8F%8A%E3%81%AE%E3%83%91%E3%83%A9%E3%83%89%E3%83%83%E3%82%AF%E3%82%B9#%E6%A7%98%E3%80%85%E3%81%AA%E8%A7%A3%E6%B1%BA%E6%A1%88)
上記のページによると、言語を階層化させるのが一つの解決策のようです。

### 階層化して書いてみる
上位の言語が下位の言語に言及する形のみを残してみました。

```prolog
% Xが第0階層 数字の大きい方を上位とする
% is_lierが第1階層

is_lier(X) :- say(X, is_lier(X)). % 第2階層 xはlierであるが、第2階層には適用されない

say(akiyama, is_lier(akiyama)).
```

実行結果
関係性が上位から下位の一方向のみになったため、trueと返してくれました。
実行過程をトレースしてみても、ループが発生していないことを確認できます。
![スクリーンショット 2021-12-02 233343.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271893/9e14ff02-8b22-c25b-2dc4-974d448919b9.png)


## 結論
Prologに自己言及のパラドックスを与えると、（手動でトレースする限りでは）延々とループして答えを出せない状態になりました。
また、上位から下位への一方向の言及のみを残すことで上記のループを解決できました。
個人的には矛盾を抱え爆発することを期待していたため、少し残念です（）。

## おまけ
試しに他のパラドックスも書いてみます。
[ワニのパラドックス](https://ja.wikipedia.org/wiki/%E3%83%AF%E3%83%8B%E3%81%AE%E3%83%91%E3%83%A9%E3%83%89%E3%83%83%E3%82%AF%E3%82%B9)
※簡潔にするため、母親だけを登場させてます。

```prolog
% ワニが母親を食べるか食べないについて
not( eat(crocodile, mother) ) :- plan( crocodile, eat(crocodile, mother) ), not( can(crocodile, eat(crocodile, mother)) ).
% ワニは母親を食べようとしている、かつ、ワニが母親を食べられないならば、ワニは母親を食べない
not( eat(crocodile, mother) ) :- plan( crocodile, not(eat(crocodile, mother)) ), not( can(crocodile, eat(crocodile, mother)) ).
% ワニは母親を食べようとしていない、かつ、ワニが母親を食べられないならば、ワニは母親を食べない
eat( crocodile, mother ) :- plan( crocodile, eat(crocodile, mother) ), can( crocodile, eat(crocodile, mother) ).
% ワニは母親を食べようとしている、かつ、ワニが母親を食べられるならば、ワニは母親を食べる
eat( crocodile, mother ) :- plan( crocodile, not(eat(crocodile, mother)) ), can( crocodile, eat(crocodile, mother) ).
% ワニは母親を食べようとしていない、かつ、ワニが母親を食べられるならば、ワニは母親を食べない

% ワニが母親を食べることができるかできないかについて
not( can(crocodile, eat(crocodile, mother)) ) :- eat( crocodile, mother ), guess( mother, plan(crocodile, eat(crocodile, mother)) ).
% ワニが母親を食べる、かつ、母親がワニは母親を食べようとしていると推測するならば、ワニは母親を食べられない
can( crocodile, eat(crocodile, mother) ) :- not( eat(crocodile, mother) ), guess( mother, plan(crocodile, eat(crocodile, mother)) ).
% ワニが母親を食べない、かつ、母親がワニは母親を食べようとしていると推測するならば、ワニは母親を食べられる

% ワニと母親に関する事実
guess( mother, plan(crocodile, eat(crocodile, mother)) ).
% 母親はワニが母親を食べようとしていると推測する
% plan( crocodile, eat(crocodile, mother) ). % どちらかを初期状態として想定
% ワニは母親を食べようとしている
plan( crocodile, not(eat(crocodile, mother)) ). % どちらかを初期状態として想定
% ワニは母親を食べようとしていない
```

実行してみると、こちらも延々とループしていました。

## 環境
Windows 10
WSL 2/Ubuntu 20.04 LTS
SWI-Prolog
https://www.swi-prolog.org/