LoginSignup
72
40

More than 3 years have passed since last update.

Prolog で SPA を書く

Last updated at Posted at 2021-01-05

"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 プログラミングについてはとりあえずわかっている前提で、多くを省略して説明しています。
悪しからずご了承ください。

fig.1

準備

"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 され) ます

/Pages/Foo.razor.prolog
% この 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 コンテンツとしてレンダリングできます。

/Pages/Foo.razor
...
<span>
  <!-- 👇 ここは "swallow" と出力される -->
  @query("canfly(Name)")
</span>

3. ユーザー入力は "事実 (fact)" に双方向バインドします

@fact("...") を使って、引数に指定した「事実 (fact)」の定義に、input 要素へのユーザー入力を双方向バインドします。

/Pages/Foo.razor
...
<input @bind='@fact("input(name, {0})").as_string' />

例えば上記 input 要素に "gopher" と入力された場合、いったんすべての input(name, _) という事実 (fact) はすべて撤回 (retract) され、代わりに input(name, "gopher") という事実 (fact) が新たに宣言 (assert) しなおされます。

4. イベントの発生を、問い合わせ (query) の呼び出しにバインドします

@then("...") を使って、イベントの発生時に、引数に指定した問い合わせ (query) が呼び出されるようにします。

/Pages/Foo.razor
...
<button @onclick='@then("check")'>Check</button>

ユーザーが上記ボタンをクリックすると、check という問い合わせ (query) が呼び出されます。

5. 事実を列挙します

<Case> コンポーネントを使って、指定した問い合わせの全ての解答 (solution) を列挙し、各解答の変数値を HTML コンテンツとしてレンダリングします。

/Pages/Foo.razor
...
<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" アプリケーションが作れる?

先に紹介したライブデモサイトで見ることができるように、例えば、"地図の塗り分け"を作って見ました。

map coloring

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 のマス) のソルバーも作ってみました。

Sudoku (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 プログラミングを楽しむ一材にでもなれば幸いです!

72
40
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
72
40