"Prolog" を知っていますか?
"Prolog" (プロログ) というのは、古くからあるプログラミング言語のひとつで、1972 年に開発されました。
Prolog はオブジェクト指向や関数型とも違う、**"論理プログラミング言語"**という枠に分類されています。
面白いことに、Prolog プログラミングにおいては、手続き型言語のように、コンピュータに動作の手順を教えるのとは違うやり方をします。
Prolog コードとして、"事実 (fact)"と"規則 (rule)"を記述するのです (下記例)。
% [事実 (fact)] ソクラテスは人である。
human(socrates).
% [規則 (rule)] 人は死ぬ。
mortal(X) :- human(X).
こうして記述された Prolog プログラムコードを、Prolog の処理系が読み込んだのち、ユーザーは処理系に対して "問い合わせ (query)" をすることができるようになります (下記例)。
% [問い合わせ (query)] 死ぬ X とは何か?
?- mortal(X).
すると、Prolog の処理系は解答 (Solution) を探索し、もし見つかればそれを応答として返す、というプログラミング言語です。
% [解答 (Solution)] 死ぬ X とはソクラテスである
X = socrates
このように Prolog はなかなかに奇妙で面白いプログラミング言語だと思います。
自分は、Prolog を知るまでは、手続きではなく論理を記述することで PC アプリケーションを作ることができるとは思い至りませんでした。
Prolog で SPA を書ける?
さてところで、自分は日頃、"Blazor" (ブレイザー) という、JavaScript ではなくて C# で Single Page Client Web Application (いわゆる SPA) を実装することのできる処理系でプログラミングをしています。
Blazor の仕組みは、Web ブラウザ上の WebAssembly エンジンを使って、.NET の中間言語 (MSIL) を実行することで実現されています。
そしてまた、自分は、Prolog 処理系を C# で実装した "C#Prolog" というライブラリを fork していました。
そんなある日、
「Blazor と C#Prolog を組み合わせたら、Prolog で SPA を書くことができるんじゃね?」
というアイディアがふと思いつきました。
そこでこの年末年始 (2020-2021) の休暇を利用して、このアイディアが実現できるか試してみることにしました。
そうしてついに、実際に Prolog で SPA を書くことを実現したのです。
"Spprologa" - Prolog で SPA を作るライブラリ
ということで、"Spprologa" と名付けた .NET 用のライブラリを公開開始しました。
この "Spprologa" ライブラリを使うことで、SPA を Prolog で実装することができるようになります。
ちなみにライブラリ名の "Spprologa" は、単純に "Single Page Prolog Application" の頭文字を取っただけですw
(発音は... スプロローガ、とかでしょうか)
ここまでの説明で既にお察しの方もいるかと思いますが、"Spprologa" は Blazor と C#Prolog の橋渡しとしてのライブラリであり、できあがるのは詰まるところ Blazor WebAssembly アプリなので、すべての Prolog コードは (サーバー側ではなく) ブラウザ上で実行されます。
Prolog コードはブラウザの WebAssembly 上で走る "C#Prolog" によって処理される仕組みであり、Prolog コードを JavaScript にトランスパイルしているわけではないです。
つまり "Spprologa" アプリケーションは、GitHub Pages, Firebase, Netlify, Azure Static Web などなど、最低限、静的コンテンツを返せる Web サーバーであれば、どこへでも配置することができるということです。
実際、下記 URL のとおり GitHub Pages 上にライブデモサイトを配置してあります。
"Spprologa" プログラミングしてみる
ということで、実際の "Spprologa" アプリケーションプログラミングがどのようなものになるのか、少し紹介したいと思います。
なお、"Spprologa" は Blazor に基づいていることから、Blazor プログラミングができることが前提となっています。
そのため、以降では Blazor プログラミングについてはとりあえずわかっている前提で、多くを省略して説明しています。
悪しからずご了承ください。
準備
"Spprologa" は Blazor に基づいているので、.NET の SDK (ver.5.0.101 かそれ以降) がインストールされている必要があります。
.NET SDK を各自お使いの PC または Mac にインストールするには、下記公式サイトを見てみてください。
.NET SDK のインストールが済んだら、続けて、下記コマンドの実行にて、"Spprologa" アプリケーションのプロジェクトテンプレートを事前にインストールしておきます。
$ dotnet new -i Spprologa.Templates::0.0.1-preview.4.0.0.0
新規 "Spprologa" プロジェクトを作って実行する
ひとたびプロジェクトテンプレートのインストールまで済んだら以降、下記のように dotnet new
コマンドを実行することで、カレントフォルダに新規 Spprologa アプリケーションプロジェクトを生成することができます。
$ dotnet new spprologa
新しい "Spprologa" アプリケーションプロジェクトが生成されたら、続けて以下のコマンドを実行することで、アプリケーションがビルド・実行開始され、さらにアプリケーションの URL が既定のブラウザで自動で開かれます。
$ dotnet watch run
あとは、この "Spprologa" アプリケーションプロジェクト上で、コーディングしてけば OK です。
発行するには
作成した "Spprologa" アプリケーションをどこかの Web サーバーに配置するには、dotnet publish
コマンドを使って、「発行」という作業をする必要があります。
具体的には下記のようにコマンド実行します。
$ dotnet publish -c:Release -o:path/to/output
発行が完了すると、その "Spprologa" アプリの実行に必要なコンテンツファイル群が path/to/output/wwwroot
フォルダに吐き出されていますので、同フォルダの内容を Web サーバーに配置すればよいです。
機能の概念
"Spprologa" アプリケーションのプログラミングは、以下の 5 つの機能に基づいています。
1. プロジェクト内の *.razor.prolog
ファイルは自動的に読み込まれ (consult され) ます
% この Prolog コードは、"Foo.razor" コンポーネントが初めてレンダリングされる1回だけ、
% Prolog エンジンに読み込まれます (consult されます)。
input(name, "").
bird("swallow", canfly).
bird("penguin", canswim).
canfly(Name) :- bird(Name, canfly).
classify(Name, "can fly") :- canfly(Name).
classify(Name, "is bird, but can't fly.") :- bird(Name, _).
classify(_, "isn't bird.").
check :-
input(name, Name), classify(Name, Message),
retractall(message(_, _)), asserta(message(Name, Message)).
2. 問い合わせ (query) の解答の変数値を、HTML にバインドします
@query("...")
を使うことで、引数に指定した問い合わせ (query) の解答 (solution) から、その変数の値を取得して、HTML コンテンツとしてレンダリングできます。
...
<span>
<!-- 👇 ここは "swallow" と出力される -->
@query("canfly(Name)")
</span>
3. ユーザー入力は "事実 (fact)" に双方向バインドします
@fact("...")
を使って、引数に指定した「事実 (fact)」の定義に、input
要素へのユーザー入力を双方向バインドします。
...
<input @bind='@fact("input(name, {0})").as_string' />
例えば上記 input
要素に "gopher" と入力された場合、いったんすべての input(name, _)
という事実 (fact) はすべて撤回 (retract) され、代わりに input(name, "gopher")
という事実 (fact) が新たに宣言 (assert) しなおされます。
4. イベントの発生を、問い合わせ (query) の呼び出しにバインドします
@then("...")
を使って、イベントの発生時に、引数に指定した問い合わせ (query) が呼び出されるようにします。
...
<button @onclick='@then("check")'>Check</button>
ユーザーが上記ボタンをクリックすると、check
という問い合わせ (query) が呼び出されます。
5. 事実を列挙します
<Case>
コンポーネントを使って、指定した問い合わせの全ての解答 (solution) を列挙し、各解答の変数値を HTML コンテンツとしてレンダリングします。
...
<ul>
<Case Query="bird(Name, Ability)">
<li>@context["Name"] - @context["Ability"]</li>
</Case>
<ul>
この例において、上記コードは以下の HTML を出力します。
<ul>
<li>swallow - canflay</li>
<li>penguin - cannotflay</li>
</ul>
どんな "Spprologa" アプリケーションが作れる?
先に紹介したライブデモサイトで見ることができるように、例えば、"地図の塗り分け"を作って見ました。
Prolog コードの中核部分の抜粋は以下のとおりです。
% define that what colors are there.
color(red).
color(green).
color(blue).
color(yellow).
% The implementation of the map coloring.
color_of_map(Alabama, Mississippi, Georgia, Tennessee, Florida) :-
color(Alabama), color(Mississippi), color(Georgia), color(Tennessee), color(Florida),
Tennessee \= Mississippi,
Tennessee \= Alabama,
Tennessee \= Georgia,
Mississippi \= Alabama,
Alabama \= Georgia,
Alabama \= Florida,
Georgia \= Florida.
その他には、"数独" (但し 9x9 ではなく 4x4 のマス) のソルバーも作ってみました。
こちらのソルバー部分の Prolog コードは下記のとおりです。
sudoku(Puzzle, Solution) :-
Solution = Puzzle,
Puzzle = [
S11, S12, S13, S14,
S21, S22, S23, S24,
S31, S32, S33, S34,
S41, S42, S43, S44
],
NUMBERS = [1,2,3,4],
% rows
permutation(NUMBERS, [S11, S12, S13, S14]),
permutation(NUMBERS, [S21, S22, S23, S24]),
permutation(NUMBERS, [S31, S32, S33, S34]),
permutation(NUMBERS, [S41, S42, S43, S44]),
% columns
permutation(NUMBERS, [S11, S21, S31, S41]),
permutation(NUMBERS, [S12, S22, S32, S42]),
permutation(NUMBERS, [S13, S23, S33, S43]),
permutation(NUMBERS, [S14, S24, S34, S44]),
% square
permutation(NUMBERS, [S11, S12, S21, S22]),
permutation(NUMBERS, [S31, S32, S41, S42]),
permutation(NUMBERS, [S13, S14, S23, S24]),
permutation(NUMBERS, [S33, S34, S43, S44]).
面白いことに、いずれの Prolog プログラムも、解き方のアルゴリズムは実装/記述していません。
代わりに、それらパズルの制約と規則を Prolog コードとして書き表しただけなのです。
それでちゃんとソルバーとしてプログラムが成り立つという点が、個人的には、Prolog の面白さであるかな、と思っています。
た・だ・し。あくまで「概念実証」プロジェクト
ここまでいろいろ書いてきましたが、しかし "Spprologa" ライブラリの開発は、「Prolog で SPA を実装できるようになるだろうか? なるとすればそれはどのような形になるだろうか?」という、あくまでも私個人の興味を満足されるために始まりました。
なので、おそらくは私の興味が尽き次第、"Spprologa" ライブラリの開発は停滞し、放置されることになるだろうなぁ、と予想していますw
しかしですね、"Spprologa" ライブラリは Mozilla Public License としてリリースしています。
つまり、いつでも・誰でも・自由に、"Spprologa" ライブラリを fork し、開発を継続しても構わないわけです。
ということで、もしそのような方がいらっしゃれば、もちろん私としては大歓迎です。👍
以上、"Spprologa" ライブラリが、Prolog プログラミングを楽しむ一材にでもなれば幸いです!