Delphi で前前前世

# はじめに
面白そうなお題を目にしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/5bf939d3-2079-c6d1-5b40-bdc5ee82df12.png)

 - [RADWIMPS (Github)](https://github.com/approvers/RADWIMPS)


これを **Delphi** でやってみます。使用する **Delphi** は **10.4 Sydney** です。

# コード

ルールを確認します。

**ルール**

 - `then().then().then().世()`のようなメソッドチェーンで "前前前世" と末尾に改行付きで出力すればOKです。
 - `Promise`や`Future`等の言語機能があればぜひ活用してください。

## ■ 基本的な考え方

**Delphi 2006** から導入された**高度なレコード型 (Advanced Record)** を使えば**メソッドチェーン (Method Chaining)** を実現できます。クラスでもいいのですが、レコード型の方がお手軽です。

```pascal
type
  TRADWIMPS = record
    function &Then: TRADWIMPS;
    function 世: TRADWIMPS;
  end;

  function TRADWIMPS.&Then: TRADWIMPS;
  begin
    Write('前');
    Result := Self;
  end;

  function TRADWIMPS.世: TRADWIMPS;
  begin
    Writeln('世');
    Result := Self;
  end;
```

`Then()` に `&` が付いているのは **Pascal** では **then** が予約語だからです。**Delphi** では予約語とカブる識別子の先頭に `&` を付けることで名前重複の問題を回避できます [^1]。この機能は他言語のライブラリなどを移植する時に重宝します。 

また、**Delphi 2009** 以降だと Unicode に対応しているので、`世()` という名前のメソッドが通ります。

**See also:**

 - [高度なレコード型 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E6%A7%8B%E9%80%A0%E5%8C%96%E5%9E%8B%EF%BC%88Delphi%EF%BC%89#.E3.83.AC.E3.82.B3.E3.83.BC.E3.83.89.E5.9E.8B.EF.BC.88.E9.AB.98.E5.BA.A6.EF.BC.89)
 - [識別子 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E6%A7%8B%E6%96%87%E8%A6%81%E7%B4%A0%EF%BC%88Delphi%EF%BC%89#.E8.AD.98.E5.88.A5.E5.AD.90)

## ■ コード例 (Delphi)

コンソールアプリケーションとして作ってみるとこんな感じになります。

```pascal:RADWIMPS.dpr
program RADWIMPS;
{$APPTYPE CONSOLE}
type
  TRADWIMPS = record
    function &Then: TRADWIMPS;
    function 世: TRADWIMPS;
  end;

  function TRADWIMPS.&Then: TRADWIMPS;
  begin
    Write('前');
    Result := Self;
  end;

  function TRADWIMPS.世: TRADWIMPS;
  begin
    Writeln('世');
    Result := Self;
  end;

begin
  var RADWIMPS: TRADWIMPS;
  RADWIMPS.&Then.&Then.&Then.世;
end. { MAIN }
```

実行してみると...エラーになりますよね？ 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/afc6c708-c9b2-1944-6bef-3222a7fc73b1.png)
**program** で プログラム名が `RADWIMPS` になっているのに、メインブロック中で識別子として **RADWIMPS** (TRADWIMPS 型) を使っているからです。これを回避するには、


```pascal:RADWIMPS.dpr
{$APPTYPE CONSOLE}
type
  TRADWIMPS = record
    function &Then: TRADWIMPS;
    function 世: TRADWIMPS;
  end;

  function TRADWIMPS.&Then: TRADWIMPS;
  begin
    Write('前');
    Result := Self;
  end;

  function TRADWIMPS.世: TRADWIMPS;
  begin
    Writeln('世');
    Result := Self;
  end;

begin
  var RADWIMPS: TRADWIMPS;
  RADWIMPS.&Then.&Then.&Then.世;
end. { MAIN }
```

先頭の **PROGRAM** ヘッダを削ります。プログラムヘッダなんて飾りです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/5b2e7c39-ecb5-ca5c-045c-1bbd7bc0e0b6.png)
今度は大丈夫でした。

### ・補足

**標準 Pascal** ではプログラム名は識別子ではないため、

```pascal
program Sin(Output);
begin
  Writeln(Sin(0));
end.
```

このようなコードも通ります (Sin は Pascal の標準関数です)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/3a1911c6-6fc7-f284-cae4-799103641f0e.png)
Delphi ではプログラムヘッダのパラメータは無視されますが、プログラム名は有効な識別子として扱われます。但し、先述のようにプログラムヘッダそのものを省略する事が可能です。

**See also:**

 - [program ヘッダー (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%A8%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88%EF%BC%88Delphi%EF%BC%89#program_.E3.83.98.E3.83.83.E3.83.80.E3.83.BC)
 - [Delphi で最少のソースコードを書いてみる (Qiita)](https://qiita.com/ht_deko/items/c81a8bacd7e299c45340)

## ■ コード例 (Free Pascal)

> クラスでもいいのですが、レコード型の方がお手軽です。

レコード型の代わりに `オブジェクト型` を使う事もできます。今回のコードだと **record** を **object** に置換するだけです。

**Delphi** における `オブジェクト型` の使用は非推奨となっていますが [^2]、**Free Pascal (FPC)** では別に使用制限されていなかったと思います。

### ・オブジェクト型を使う場合

**FPC** で`オブジェクト型` を使ったコードは次のようになります。

```pascal
type
  TRADWIMPS = object
    function &Then: TRADWIMPS;
    function Se: TRADWIMPS;
  end;

  function TRADWIMPS.&Then: TRADWIMPS;
  begin
    Write('前');
    &Then := Self;
  end;

  function TRADWIMPS.Se: TRADWIMPS;
  begin
    Writeln('世');
    Se := Self;
  end;
  
var 
  RADWIMPS: TRADWIMPS;
begin
  RADWIMPS.&Then.&Then.&Then.Se;
end. { MAIN }
```

 - レコード型の代わりにオブジェクト型を使っている。
 - `世()` は使えないので `Se()` に変更。
 - Result 変数が使えないのでルーチン名に値を代入して結果を返している。
 - インライン変数宣言ができないので **var** ブロックで変数宣言。

### ・高度なレコード型を使う場合

**FPC** での高度なレコード型はオプション扱いなので、高度なレコード型を使いたい場合にはコンパイラ指令 `$modeswitch` に `advancedrecords` を指定します。

どうせコンパイラ指令を追加しなくてはならないのなら、`{$mode objfpc}` (と `{$H+}`) を追加すると **Object Pascal** に対応するので、Result 変数が使えるようになって便利です。

```pascal
{$mode objfpc}{$H+}{$modeswitch advancedrecords}
type
  TRADWIMPS = record
    function &Then: TRADWIMPS;
    function Se: TRADWIMPS;
  end;

  function TRADWIMPS.&Then: TRADWIMPS;
  begin
    Write('前');
    Result := Self;
  end;

  function TRADWIMPS.Se: TRADWIMPS;
  begin
    Writeln('世');
    Result := Self;
  end;
  
var 
  RADWIMPS: TRADWIMPS;
begin
  RADWIMPS.&Then.&Then.&Then.Se;
end. { MAIN }
```

`{$mode objfpc}{$H+}{$modeswitch advancedrecords}` の部分を `{$mode delphi}` に変更し、 **Delphi** 互換モードで動作させる事も可能です。

### ・スクリプトとして実行

**FPC** をインストールすると、スクリプト実行環境である **InstantFPC** もインストールされているため、コードをスクリプトとして実行させる事が可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/fcc61b45-d950-f1cb-fa51-82d7eeeb02ef.png)
コードの先頭に `#!/usr/bin/env instantfpc` を追加し、実行権限を与えるだけなのでお手軽です。

### ・補足

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/8df2cec4-e11b-2a0c-ee2b-e7d582ee4a60.png)
**Free Pascal** のコードは [ideone.com](https://ideone.com/) で試す事ができます。**Pascal** は 2 種類あるので、試す際には `FPC` の方を選んでください。

**See also:**

 - [<6> Dephi のオブジェクト指向拡張 (Pascal へのオブジェクト指向拡張の歴史と Delphi) (Qiita)](https://qiita.com/ht_deko/items/28fc0af9fc9e34a8d5c5)
 - [Lazarus (lazarus-ide.org)](https://www.lazarus-ide.org/)
 - [InstantFPC (Free Pascal Wiki)](https://wiki.lazarus.freepascal.org/InstantFPC)
 - [ideone.com](https://ideone.com/)
 - [Mode Delphi (Lazarus Wiki)](https://wiki.freepascal.org/Mode_Delphi)


## ■ コード例 (MPW Pascal)

**Object Pascal** の始祖である **MPW Pascal (Apple Object Pascal)** でもメソッドチェーンは可能です。

### ・一般的なコード 

一般的なコードはこうなります。

```pascal:RADWIMPS.p
program RADWIMPS;

uses
  ObjIntf;

type
  TRADWIMPS = object(TObject)
    function _Then: TRADWIMPS;
    procedure Se;
  end;

  function TRADWIMPS._Then: TRADWIMPS;
  begin
    Write('Zen');
    _Then := SELF;
  end;
  
  procedure TRADWIMPS.Se;
  begin
    WriteLn('Se');
  end;

var
  RADWIMPS: TRADWIMPS;
begin
  New(RADWIMPS);
  RADWIMPS._Then._Then._Then.Se;
  RADWIMPS.Free;
end. { MAIN }
```

 - **PROGRAM** ヘッダがないと怒られるので追加。但し、プログラム名は識別子とみなされません。
 - クラスを使います。
 - TObject を使うので **uses** に `ObjIntf` を追加。
 - 日本語が使えません (使い方がわかりません)。
 - Result 変数が使えないので、メソッド名に結果を返しています。
 - **Then** は予約語ですが、`&` による重複回避ができないので `_Then()` にしてお茶を濁しています。
 - 関数の結果を読み捨てられないので、メソッド `Se()` は手続き (**procedure**) で実装しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/47a5078c-b8cb-1c11-fb08-1b12ffae0113.png)


### ・TObject から派生しないコード

クラスを TObject から派生しなければちょっと短く書けます。

```pascal:RADWIMPS.p
program RADWIMPS;

type
  TRADWIMPS = object
    function _Then: TRADWIMPS;
    procedure Se;
  end;

  function TRADWIMPS._Then: TRADWIMPS;
  begin
    Write('Zen');
    _Then := SELF;
  end;
  
  procedure TRADWIMPS.Se;
  begin
    WriteLn('Se');
  end;

var
  RADWIMPS: TRADWIMPS;
begin
  New(RADWIMPS);
  RADWIMPS._Then._Then._Then.Se;
  Dispose(RADWIMPS);
end. { MAIN }
```

短く書けますが、`Free()` メソッドは使えなくなるので、破棄処理は `Dispose()` で行う必要があります。

### ・補足

**FPC** で試す場合には **PROGRAM** ヘッダと **uses** 句を削除した上で、`{$MODE MacPas}` コンパイラ指令を先頭に付けてください。 


**See also:**

 - [<3> Macintosh 用 Pascal のオブジェクト指向拡張 (Pascal へのオブジェクト指向拡張の歴史と Delphi) (Qiita)](https://qiita.com/ht_deko/items/cd245180363e1911afa7)
 - [Object Pascal (Wikipedia)](https://ja.wikipedia.org/wiki/Object_Pascal)
 - [ラリー・テスラー (Wikipedia)](https://ja.wikipedia.org/wiki/%E3%83%A9%E3%83%AA%E3%83%BC%E3%83%BB%E3%83%86%E3%82%B9%E3%83%A9%E3%83%BC)
 - [Mode MacPas (Lazarus Wiki)](https://wiki.freepascal.org/Mode_MacPas)

# おわりに
Delphi でのメソッドチェーンといえば、大作があるのです。Jim McKeeth 氏作の**『Code Monkey』**です。
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/0623e2f3-07b9-9321-fc0b-6a91eaa7d124.png)](https://www.youtube.com/watch?v=-nGvMbQKS7U)
**まずはこれを観て！**

 - [Code Monkey as Code in Delphi (Youtube)](https://www.youtube.com/watch?v=-nGvMbQKS7U)

ソースコードは Jim 氏のサイトからダウンロードできます。

 - [Code Monkey Code (The Podcast at Delphi.org)](http://delphi.org/CodeMonkey/)

メソッドチェーンできる高度なレコード型を使って、コード補完でデタラメに入力しているのではなく、ちゃんとしたプログラムになっており、このコードは実際に実行可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/8251db1e-35cd-8d00-373f-27bd45fc3cfb.png)
実行すると歌詞が流れます。

[^1]: `&` による識別子の重複回避は **Delphi 2005** から使えます。
[^2]: **Delphi** では比較的新しいバージョンでないとオブジェクト型によるメソッドチェーンはできません。具体的には **Delphi XE5** 以降でないと正しく動作しません。非推奨と言いながら不具合修正されているのは面白いですね。なお、クラスによるメソッドチェーンは **Delphi 1** から可能です。