fluorite-7　日本語チュートリアル　Part 10　クラスとメソッド


[チュートリアルトップ](https://qiita.com/MirrgieRiana/items/a0906c72ebfb46693ab5)

[←前回](https://qiita.com/MirrgieRiana/items/e35826c75c3d77fe9146)　[次回→](https://qiita.com/MirrgieRiana/items/ac24970bc8e2a384ebc0)

このパートでは、オブジェクト指向の中心的機構であるクラスとメソッドについて取り扱います。

# レッスン1　クラス

fluorite-7では、「クラス名`: {` `};`」で**クラス**を生成し、宣言します。

```bash:
$ fl7 'Human: {}; Human'
{}
```

クラスを**インスタンス化**するには、クラスに続けて波括弧`{` `}`を記述します。

```bash:
$ fl7 'Human: {}; Human{}'
{}
```

# レッスン2　メソッド

クラスメソッドを定義するには、クラスを定義する際に、波括弧の中で「メソッド名`: _, `引数列`->`式」と記述します。
クラスメソッドを利用するには、「インスタンス`::`メソッド名`(`引数列`)`」と記述します。

```bash:
$ fl7 'Human: {attack: _, target -> "$(target)を殴りつけた。";}; Human{}::attack("スライム")'
スライムを殴りつけた。
```

```perl:
Human: {
  attack: _, target -> "$(target)を殴りつけた。";
};

Human{}::attack("スライム")
```

[オンラインエディタで見る](https://mirrgieriana.github.io/fluorite-7/editor/?s=%0D%0AHuman%3A%20%7B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%22%24(target)%E3%82%92%E6%AE%B4%E3%82%8A%E3%81%A4%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AHuman%7B%7D%3A%3Aattack(%22%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%A0%22)%0D%0A)

# レッスン3　コンストラクタ

クラスの定義中に「`new: _ -> _{};`」と記述すると、「クラス名`::new()`」という形で**コンストラクタ**を呼び出せます。

```bash:
$ fl7 'Human: {new: _ -> _{}; attack: _, target -> "$(target)を殴りつけた。";}; Human::new()::attack("スライム")'
スライムを殴りつけた。
```

```perl:
Human: {
  new: _ -> _{};
  attack: _, target -> "$(target)を殴りつけた。";
};

Human::new()::attack("スライム")
```

[オンラインエディタで見る](https://mirrgieriana.github.io/fluorite-7/editor/?s=%0D%0AHuman%3A%20%7B%0D%0A%20%20new%3A%20_%20-%3E%20_%7B%7D%3B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%22%24(target)%E3%82%92%E6%AE%B4%E3%82%8A%E3%81%A4%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AHuman%3A%3Anew()%3A%3Aattack(%22%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%A0%22)%0D%0A)

# レッスン4　フィールド

インスタンス化時に**フィールド**を定義するには、コンストラクタ内の`_{`と`}`の間で、「フィールド名`=`値」を`;`で区切って並べればよいです。
`;`は無駄に多く書いても、単に無視されます。
フィールドにアクセスするには、「インスタンス`.`フィールド名」と記述します。

```bash:
$ fl7 'Human: {new: _ -> _{name = "大石";}; attack: _, target -> "$(target)を殴りつけた。";}; Human::new().name'
大石
```

```perl:
Human: {
  new: _ -> _{
    name = "大石";
  };
  attack: _, target -> "$(target)を殴りつけた。";
};

Human::new().name
```

[オンラインエディタで見る](https://mirrgieriana.github.io/fluorite-7/editor/?s=%0D%0AHuman%3A%20%7B%0D%0A%20%20new%3A%20_%20-%3E%20_%7B%0D%0A%20%20%20%20name%20%3D%20%22%E5%A4%A7%E7%9F%B3%22%3B%0D%0A%20%20%7D%3B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%22%24(target)%E3%82%92%E6%AE%B4%E3%82%8A%E3%81%A4%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AHuman%3A%3Anew().name%0D%0A)

# レッスン5　this

メソッド内では、変数`_`により呼び出しコンテキストに紐づいたインスタンス、所謂thisを得ることができます。

```bash:
$ fl7 'Human: {new: _ -> _{name = "大石";}; attack: _, target -> "$(_.name)は$(target)を殴りつけた。";}; Human::new()::attack("スライム")'
大石はスライムを殴りつけた。
```

```perl:
Human: {
  new: _ -> _{
    name = "大石";
  };
  attack: _, target -> "$(_.name)は$(target)を殴りつけた。";
};

Human::new()::attack("スライム")
```

[オンラインエディタで見る](https://mirrgieriana.github.io/fluorite-7/editor/?s=%0D%0AHuman%3A%20%7B%0D%0A%20%20new%3A%20_%20-%3E%20_%7B%0D%0A%20%20%20%20name%20%3D%20%22%E5%A4%A7%E7%9F%B3%22%3B%0D%0A%20%20%7D%3B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%22%24(_.name)%E3%81%AF%24(target)%E3%82%92%E6%AE%B4%E3%82%8A%E3%81%A4%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AHuman%3A%3Anew()%3A%3Aattack(%22%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%A0%22)%0D%0A)

# レッスン6　コンストラクタの引数

コンストラクタ``new: _ -> _{};``は、`_`と`->`の間に「`,`引数名」を並べることで、引数を取ることができます。

```bash:
$ fl7 'Human: {new: _, name -> _{name = name;}; attack: _, target -> "$(_.name)は$(target)を殴りつけた。";}; Human::new("大石")::attack("スライム")'
大石はスライムを殴りつけた。
```

```perl:
Human: {
  new: _, name -> _{
    name = name;
  };
  attack: _, target -> "$(_.name)は$(target)を殴りつけた。";
};

Human::new("大石")::attack("スライム")
```

[オンラインエディタで見る](https://mirrgieriana.github.io/fluorite-7/editor/?s=%0D%0AHuman%3A%20%7B%0D%0A%20%20new%3A%20_%2C%20name%20-%3E%20_%7B%0D%0A%20%20%20%20name%20%3D%20name%3B%0D%0A%20%20%7D%3B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%22%24(_.name)%E3%81%AF%24(target)%E3%82%92%E6%AE%B4%E3%82%8A%E3%81%A4%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AHuman%3A%3Anew(%22%E5%A4%A7%E7%9F%B3%22)%3A%3Aattack(%22%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%A0%22)%0D%0A)

# レッスン7　クラスの継承

「子クラス名`: `親クラス名`{` `};`」で、親クラスを継承したクラスを作り、宣言することができます。
子クラスのコンストラクタでは、`_{}`の代わりに「`^.new(_)`」または「`^.new(_;`引数列`)`」のようにして親クラスのコンストラクタを呼んでください。

```bash:
$ fl7 'Human: {new: _, name -> _{name = name;}; attack: _, target -> "$(_.name)は$(target)を殴りつけた。";}; Magician: Human{new: _, name -> ^.new(_; name);}; Human::new("大石")::attack("スライム")'
大石はスライムを殴りつけた。
```

```perl:
Human: {
  new: _, name -> _{
    name = name;
  };
  attack: _, target -> "$(_.name)は$(target)を殴りつけた。";
};

Magician: Human{
  new: _, name -> ^.new(_; name);
};

Magician::new("大石")::attack("スライム")
```

[オンラインエディタで見る](https://mirrgieriana.github.io/fluorite-7/editor/?s=%0D%0AHuman%3A%20%7B%0D%0A%20%20new%3A%20_%2C%20name%20-%3E%20_%7B%0D%0A%20%20%20%20name%20%3D%20name%3B%0D%0A%20%20%7D%3B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%22%24(_.name)%E3%81%AF%24(target)%E3%82%92%E6%AE%B4%E3%82%8A%E3%81%A4%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AMagician%3A%20Human%7B%0D%0A%20%20new%3A%20_%2C%20name%20-%3E%20%5E.new(_%3B%20name)%3B%0D%0A%7D%3B%0D%0A%0D%0AMagician%3A%3Anew(%22%E5%A4%A7%E7%9F%B3%22)%3A%3Aattack(%22%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%A0%22)%0D%0A)

# レッスン8　メソッドのオーバーライド

子クラスで同名のメソッドを定義すると、メソッドをオーバーライドできます。

```bash:
$ fl7 'Human: {new: _, name -> _{name = name;}; attack: _, target -> "$(_.name)は$(target)を殴りつけた。";}; Magician: Human{new: _, name -> ^.new(_; name); attack: _, target -> "$(_.name)は$(target)に魔法をかけた。";}; Human::new("大石")::attack("スライム"), Magician::new("大石")::attack("スライム"),'
大石はスライムを殴りつけた。
大石はスライムに魔法をかけた。
```

```perl:
Human: {
  new: _, name -> _{
    name = name;
  };
  attack: _, target -> "$(_.name)は$(target)を殴りつけた。";
};

Magician: Human{
  new: _, name -> ^.new(_; name);
  attack: _, target -> "$(_.name)は$(target)に魔法をかけた。";
};

Human::new("大石")::attack("スライム"),
Magician::new("大石")::attack("スライム"),
```

[オンラインエディタで見る](https://mirrgieriana.github.io/fluorite-7/editor/?s=%0D%0AHuman%3A%20%7B%0D%0A%20%20new%3A%20_%2C%20name%20-%3E%20_%7B%0D%0A%20%20%20%20name%20%3D%20name%3B%0D%0A%20%20%7D%3B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%22%24(_.name)%E3%81%AF%24(target)%E3%82%92%E6%AE%B4%E3%82%8A%E3%81%A4%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AMagician%3A%20Human%7B%0D%0A%20%20new%3A%20_%2C%20name%20-%3E%20%5E.new(_%3B%20name)%3B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%22%24(_.name)%E3%81%AF%24(target)%E3%81%AB%E9%AD%94%E6%B3%95%E3%82%92%E3%81%8B%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AHuman%3A%3Anew(%22%E5%A4%A7%E7%9F%B3%22)%3A%3Aattack(%22%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%A0%22)%2C%0D%0AMagician%3A%3Anew(%22%E5%A4%A7%E7%9F%B3%22)%3A%3Aattack(%22%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%A0%22)%2C%0D%0A)

# レッスン9　親クラスのメソッド呼び出し

「`^.`メソッド名`(_)`」または「`^.`メソッド名`(_;`引数列`)`」で親クラスのメソッドを呼び出すことができます。

```bash:
$ fl7 'Human: {new: _, name -> _{name = name;}; attack: _, target -> "$(_.name)は$(target)を殴りつけた。";}; Magician: Human{new: _, name -> ^.new(_; name); attack: _, target -> ^.attack(_; target) & "さらに魔法もかけた。";}; Human::new("大石")::attack("スライム"), Magician::new("大石")::attack("スライム"),'
大石はスライムを殴りつけた。
大石はスライムを殴りつけた。さらに魔法もかけた。
```

```perl:
Human: {
  new: _, name -> _{
    name = name;
  };
  attack: _, target -> "$(_.name)は$(target)を殴りつけた。";
};

Magician: Human{
  new: _, name -> ^.new(_; name);
  attack: _, target -> ^.attack(_; target) & "さらに魔法もかけた。";
};

Human::new("大石")::attack("スライム"),
Magician::new("大石")::attack("スライム"),
```

[オンラインエディタで見る](https://mirrgieriana.github.io/fluorite-7/editor/?s=%0D%0AHuman%3A%20%7B%0D%0A%20%20new%3A%20_%2C%20name%20-%3E%20_%7B%0D%0A%20%20%20%20name%20%3D%20name%3B%0D%0A%20%20%7D%3B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%22%24(_.name)%E3%81%AF%24(target)%E3%82%92%E6%AE%B4%E3%82%8A%E3%81%A4%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AMagician%3A%20Human%7B%0D%0A%20%20new%3A%20_%2C%20name%20-%3E%20%5E.new(_%3B%20name)%3B%0D%0A%20%20attack%3A%20_%2C%20target%20-%3E%20%5E.attack(_%3B%20target)%20%26%20%22%E3%81%95%E3%82%89%E3%81%AB%E9%AD%94%E6%B3%95%E3%82%82%E3%81%8B%E3%81%91%E3%81%9F%E3%80%82%22%3B%0D%0A%7D%3B%0D%0A%0D%0AHuman%3A%3Anew(%22%E5%A4%A7%E7%9F%B3%22)%3A%3Aattack(%22%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%A0%22)%2C%0D%0AMagician%3A%3Anew(%22%E5%A4%A7%E7%9F%B3%22)%3A%3Aattack(%22%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%A0%22)%2C%0D%0A)

# レッスン10　種明かし

fluorite-7には言語仕様としてはクラスもメソッドも存在しません。
今回新たに登場した文法は以下のものだけです。

- 「オブジェクト`::`メンバ名」で、メンバ関数の第1引数にオブジェクトを部分適用した関数を生成する。
- オブジェクト初期化子の中で`^`と書くと親オブジェクトが得られる。

`::`演算子を**デリゲート演算子**、デリゲート演算子を適用することを**デリゲート生成**と呼びます。
`^`は**親オブジェクト参照子**と呼びます。

fluorite-7のオブジェクト指向は、継承付きオブジェクトとデリゲートという単純な機構を組み合わせて出来ています。

また、`^`に似た機能の**オブジェクト参照子**`$`もあります。

- オブジェクト初期化子の中で`$`と書くとそのオブジェクトが得られる。

```bash:
$ fl7 'obj: {a: 1}{a: 2; p: () -> ^; o: () -> $}; obj.p().a, obj.o().a,'
1
2
```

```perl:
obj: {
  a: 1
}{
  a: 2;
  p: () -> ^;
  o: () -> $;
};

obj.p().a,
obj.o().a,
```

[オンラインエディタで見る](https://mirrgieriana.github.io/fluorite-7/editor/?s=%0D%0Aobj%3A%20%7B%0D%0A%20%20a%3A%201%0D%0A%7D%7B%0D%0A%20%20a%3A%202%3B%0D%0A%20%20p%3A%20()%20-%3E%20%5E%3B%0D%0A%20%20o%3A%20()%20-%3E%20%24%3B%0D%0A%7D%3B%0D%0A%0D%0Aobj.p().a%2C%0D%0Aobj.o().a%2C%0D%0A)

# レッスン11　サンプルコードの解釈

それでは、以上のことを踏まえたうえで最後に登場した`Human`クラスの登場するサンプルコードを見てみましょう。

```perl:
Human: {
  new: _, name -> _{
    name = name;
  };
  attack: _, target -> "$(_.name)は$(target)を殴りつけた。";
};

Magician: Human{
  new: _, name -> ^.new(_; name);
  attack: _, target -> ^.attack(_; target) & "さらに魔法もかけた。";
};

Human::new("大石")::attack("スライム"),
Magician::new("大石")::attack("スライム"),
```

## クラスの定義

まず`Human: {`～`};`という記述は、新しいオブジェクトで初期化された`Human`という変数を宣言しただけにほかなりません。
`Magician: Human{`～`};`という記述も、変数`Human`に格納されたオブジェクトを継承した新しいオブジェクトを作り、`Magician`と名付けただけにほかなりません。
オブジェクトが継承する際には親オブジェクトのプロパティがメンバとして見えるようになるため、クラスの継承と同じ機能が実現されます。

fluorite-7のクラスは継承付きオブジェクトで実現されます。

## メソッド

```perl:
{
  attack: _, target -> "$(_.name)は$(target)を殴りつけた。";
}
```

この部分では、オブジェクトに対して`attack`という名前のキーで`_, target -> "$(_.name)は$(target)を殴りつけた。"`という値を持つプロパティを宣言しています。
この値は`_`と`target`を引数に取って`"$(_.name)は$(target)を殴りつけた。"`を返す関数です。

fluorite-7のメソッドはクラスに格納された関数です。

## 根底クラスのコンストラクタ

```perl:
new: _, name -> _{
  name = name;
};
```

コンストラクタも関数が格納されたプロパティです。
コンストラクタの本文には、その関数の引数を使用した式である`_{name = name;}`と書かれています。

実はコンストラクタ名は`new`以外でも何でもよいです。

## 根底クラスのコンストラクタの呼び出し

```perl:
Human: {
  new: _, name -> _{
    name = name;
  };
};

Human::new("大石")
```

オブジェクト`Human`に対して、`::new`を付けるとどうなるでしょうか。

デリゲート演算子`::`の説明は次のものでした。

> 「オブジェクト`::`メンバ名」で、メンバ関数の第1引数にオブジェクトを部分適用した関数を生成する。

メンバ`new`が表す関数は`_, name -> _{name = name;}`です。
これの第1引数に`Human`を部分適用すると、`name -> Human{name = name;}`となります。
これはクラス`Human`の動作を継承したインスタンスを生成していることになります。
また、インスタンスが持つフィールドとして、引数`name`を`name`に代入しています。

コンストラクタの第1引数`_`には、クラスがやってくるということになります。

----

`Human::new`は`name -> Human{name = name;}`と等価でした。
この関数`("大石")`を適用すると、`Human{name = "大石";}`が得られます。
これは`name`というプロパティつきの、クラス`Human`を親として持つオブジェクトです。

コンストラクタの第2引数以降には、`::new()`する際の引数がやってくることになります。

## 親クラスのコンストラクタの呼び出し

子クラス`Magician`のコンストラクタにおける親クラスのコンストラクタ呼び出しは次のようでした。

```perl:
Human: {
  new: _, name -> _{
    name = name;
  };
};

Magician: Human{
  new: _, name -> ^.new(_; name);  // ←ここ
};

Human::new("大石")
```

まず、`_`には「実際のクラス」がやってきます。
それは、このコンストラクタが定義されているクラスかもしれませんし、その更に子クラスかもしれません。
実際のクラスが分からないため、引数で受け取ります。
そして、根底クラスでは実際のクラスである`_`を親としてインスタンスオブジェクトを生成しています。

`Magician`クラスの初期化子の中に書かれている`^`で得られるのは、親オブジェクトである`Human`クラスです。
`^.new`は`Human`クラスのメンバ関数`new`です。
さっきまでは`new`はデリゲート演算子`::`で呼び出していましたが、ここではメンバアクセサ`.`で呼び出しています。
それは、`Human::new`はクラス`Human`のインスタンスを生成してしまうからです。
`new`の第1引数には実際のクラスを指定しなければなりません。
それには、`new`そのものを参照し、実際のクラスである`_`を明示的に与えればよいです。
それに残りの引数である`name`を与えたものが`^.new(_; name)`です。

結果、`Magician::new("大石")`という呼び出しでは`Magician`クラスを親オブジェクトとしたインスタンスオブジェクトが得られます。

## メソッド呼び出し

```perl:
Human: {
  new: _, name -> _{
    name = name;
  };
  attack: _, target -> "$(_.name)は$(target)を殴りつけた。";
};

Magician: Human{
  new: _, name -> ^.new(_; name);
  attack: _, target -> ^.attack(_; target) & "さらに魔法もかけた。";
};

Magician::new("大石")::attack("スライム"),
```

このコードでは、`Magician`クラスのインスタンスである`Magician::new("大石")`のメソッド`attack`を、`"スライム"`という引数で呼び出しています。

`Magician::new("大石")::attack`の時点で何が起こるのかというと、`Magician`クラスのインスタンスのメンバ`attack`を参照し、第1引数にそのインスタンスを部分適用しています。
`Magician`クラスのインスタンスは、`Magician`クラスを継承しているので、`Magician`クラスの`attack`メソッドがメンバ`attack`として見えています。
もし`Magician`クラスに`attack`メソッドがなかった場合、`Human`クラスの`attack`メソッドが見えます。

`Magician`クラスの`attack`メソッドとは、`_, target -> ^.attack(_; target) & "さらに魔法もかけた。"`のことです。
これは、インスタンスである`_`ともう一つの引数`target`を受け取ると、`Magician`クラスの親オブジェクトである`Human`クラスのメンバ`attack`を、インスタンス`_`と引数`target`で起動し、その結果を文字列`"さらに魔法もかけた。"`と結合して返します。
`Human`クラスが持つ`attack`メソッド関数`_, target -> "$(_.name)は$(target)を殴りつけた。"`は、`_`にインスタンスが、`target`には`"スライム"`が指定されて呼び出されます。
`_.name`はインスタンスのメンバ`name`ですが、これは`Human`クラスのコンストラクタ`new: _, name -> _{name = name;};`で代入したプロパティ`name`がフィールドとして見えています。
結果、`Human`クラスの`attack`メソッドは`"大石はスライムを殴りつけた。"`を返します。
`Magician`クラスの`attack`メソッド`^.attack(_; target) & "さらに魔法もかけた。"`の前半部分`^.attack(_; target)`は`"大石はスライムを殴りつけた。"`となるので、後半の`"さらに魔法もかけた。"`と文字列結合して`"大石はスライムを殴りつけた。さらに魔法もかけた。"`が返ります。

最終的に、`Magician::new("大石")::attack("スライム")`は`"大石はスライムを殴りつけた。さらに魔法もかけた。"`となります。

# まとめ

長くなりましたが、fluorite-7にはオブジェクト指向を成立させる機構が存在ます。

- デリゲート演算子`object::method`は、`object.method`の第1引数に`object`を部分適用した関数を得る。
- オブジェクト初期化子の中で`$`と書くと、そのオブジェクトを得る。
- オブジェクト初期化子の中で`^`と書くと、親オブジェクトを得る。

# Part 11

[次回→](https://qiita.com/MirrgieRiana/items/ac24970bc8e2a384ebc0)