fluorite-7　日本語リファレンス　オブジェクト指向


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

# 基礎

`{` `}`でオブジェクトが作れる。

```perl:
{
  key : "value";
}
```

オブジェクトの値は`object.key`で得られる。

```perl:
{
  key : "value";
}.key
```

```bash:
$ fl7 '{key : "value"}.key'
value
```

オブジェクトに`{` `}`を後置すると継承したオブジェクトが作れる。
`object.key`では親の値も見える。

```perl:
parent : {
  key : "value";
};
obj : parent {
  key2 : "value2";
};
obj.key, obj.key2
```

```bash:
$ fl7 'parent : {key : "value"}; obj : parent {key2 : "value2"}; obj.key, obj.key2'
value
value2
```

これを利用すると「クラス」が作れる。

`object::method(args)`で、`object.method(object; args)`の動きをする。

```perl:
Obj : {
  get_value : this -> this.value;
};
obj : Obj {
  value : "value!!!";
};
obj::get_value()
```

```bash:
$ fl7 'Obj : {get_value : this -> this.value}; obj : Obj {value : "value!!!"}; obj::get_value()'
value!!!
```

# オブジェクト生成

## オブジェクト生成演算子

オブジェクト生成演算子`{entries}`は、オブジェクトを生成する括弧類である。
`entries`には`;`区切りで0個以上のエントリーを記述する。

### 宣言エントリー

`key : value`は`key`という名前を宣言しつつキーに`value`を代入するエントリーである。
`key`は宣言されたオブジェクト内のどこからでも参照できる。

```perl:
{
  x : y - 1;
  y : 100;
  z : y + 1;
}
```

```bash:
$ fl7 '{x : y - 1; y : 100; z : y + 1}'
{x:99;y:100;z:101}
```

これを使って循環参照を行うこともできる。

```perl:
obj : {
  f : n -> n === 0 ? 1 : f(n - 1) * n;
};
obj.f(5)
```

```bash:
$ fl7 'obj : {f : n -> n === 0 ? 1 : f(n - 1) * n}; obj.f(5)'
120
```

`key`は数値・文字列リテラル・括弧であってもよい。
その場合、文字列に変換されてキーとして認識される。

```perl:
{
  E : "identifier";
  100 : "number";
  "#" : "string";
  (PI) : "formula";
}
```

```bash:
$ fl7 '{E : "identifier"; 100 : "number"; "#" : "string"; (PI) : "formula"}'
{100:number;E:identifier;#:string;3.141592653589793:formula}
```

### 代入エントリー

`key = value`は`key`というキーに`value`を代入するエントリーである。

```perl:
x : 20;
{
  x = x;
}
```

```bash:
$ fl7 'x : 20; {x = x}'
{x:20}
```

代入エントリーでは名前を宣言しないため、意図せず循環参照に陥ることがない。

その他のことは概ね宣言エントリーと同様である。

### 即席代入エントリー

`key`は`key`というキーに`key`を代入するエントリーである。

```perl:
x : 20;
{
  x;
}
```

```bash:
$ fl7 'x : 20; {x}'
{x:20}
```

その他のことは概ね代入エントリーと同様である。

**Tips** 即席代入はコンストラクタメソッド内でよく使われる。

```perl:
Obj : {
  new : class, value -> class {
    value;
  };
  get_value : this -> this.value;
};
Obj::new("Value!")::get_value()
```

```bash:
$ fl7 'Obj : {new : class, value -> class {value}; get_value : this -> this.value}; Obj::new("Value!!!")::get_value()'
Value!!!
```

## 継承つきオブジェクト生成演算子

継承つきオブジェクト生成演算子`parent {entries}`は、`parent`を継承したオブジェクトを生成する**後置演算子**である。
`entries`は、オブジェクト生成演算子`{entries}`と同じ仕様を持つ。
継承つきオブジェクト生成演算子の結合優先度はその他の後置演算子と等しい。
その他の挙動は、概ねオブジェクト生成演算子と等しい。

### 親オブジェクト参照子

親オブジェクト参照子`^`は、オブジェクト内でその親オブジェクトを参照できる式である。

```perl:
parent : {
  a : 10;
};
obj : parent {
  a : 20;
  c : ^.a;
};
obj.c
```

```bash:
$ fl7 'parent : {a : 10}; obj : parent {a : 20; c : ^.a}; obj.c'
10
```

### クラスの表現ができる

継承つきオブジェクト生成演算子は、オブジェクト指向におけるClass-Object間の関係を表すのに使うことができる。

```perl:
Obj : {
  new : class, value -> class {
    value;
  };
  get_value : this -> this.value;
};
Obj::new("Value!!!")::get_value()
```

```bash:
$ fl7 'Obj : {new : class, value -> class {value}; get_value : this -> this.value}; Obj::new("Value!!!")::get_value()'
Value!!!
```

### クラスの継承の表現ができる

継承つきオブジェクト生成演算子は、オブジェクト指向におけるSuper Class-Sub Class間の関係を表すのに使うことができる。

```perl:
Obj : {
  new : class, value -> class {
    value;
  };
  get_value : this -> this.value;
};
SubObj : Obj {
  new : class, value -> ^.new(class; value);
  get_value : this -> ^.get_value(this) * 2;
};
SubObj::new("Value!!!")::get_value()
```

```bash:
$ fl7 'Obj : {new : class, value -> class {value}; get_value : this -> this.value}; SubObj : Obj {new : class, value -> ^.new(class; value); get_value : this -> ^.get_value(this) * 2}; SubObj::new("Value!!!")::get_value()'
Value!!!Value!!!
```

----

上記のコードの`SubObj::new("Value!!!")::get_value()`は次のように等価に変形できる。

次のコードは変形前の状態である。

```perl:
SubObj::new("Value!!!")::get_value()
```

`SubObj::new("Value!!!")`は、`SubObj.new(SubObj; "Value!!!")`に変形できる。

```perl:
SubObj.new(SubObj; "Value!!!")::get_value()
```

`SubObj.new(SubObj; "Value!!!")`を展開する。

```perl:
Obj.new(SubObj; "Value!!!")::get_value()
```

`Obj.new(SubObj; "Value!!!")`を展開する。

```perl:
obj : SubObj {
  value = "Value!!!";
};
obj::get_value()
```

`obj::get_value()`を展開する。

```perl:
obj : SubObj {
  value = "Value!!!";
};
obj.get_value(obj)
```

`SubObj`の子`obj`自身にはプロパティ`get_value`が存在しないため、一つ上の親である`SubObj`が持つプロパティ`get_value`がヒットする。
`obj.get_value`は`SubObj.get_value`で置換できる。

```perl:
obj : SubObj {
  value = "Value!!!";
};
SubObj.get_value(obj)
```

`SubObj.get_value`を展開する。

```perl:
obj : SubObj {
  value = "Value!!!";
};
Obj.get_value(obj) * 2
```

`Obj.get_value`を展開する。

```perl:
obj : SubObj {
  value = "Value!!!";
};
obj.value * 2
```

`obj`はプロパティ`value`を持っており、その値はここでは`"Value!!!"`である。
これを`obj.value`に代入する。

```perl:
"Value!!!" * 2
```

`"Value!!!" * 2`を計算する。

```perl:
"Value!!!Value!!!"
```

fl7におけるオブジェクトの継承はこのようにして成り立っている。

## エントリーストリームからの生成

エントリーストリームからのオブジェクト生成演算子`{[entries]}`は、`entries`をエントリーのストリームと解釈し、それらで構成されるオブジェクトを生成する括弧類である。

```perl:
{[
  1 .. 5 | {
    key : _;
    value : _ * 11;
  }
]}
```

```bash:
$ fl7 '{[1 .. 5 | {key : _; value : _ * 11}]}'
{1:11;2:22;3:33;4:44;5:55}
```

# アクセス

## メンバアクセス

メンバアクセス演算子`object.key`は、`object`のメンバ`key`を取得する**後置演算子**である。
ここで`key`は識別子の名前そのものを表す。
結合優先度はその他の後置演算子と等しい。

メンバアクセスではキーが存在しなかった場合、親オブジェクトを再帰的に遡ってヒットするキーを検索する。

全くヒットしなかった場合、`NULL`が返る。

```perl:
obj : {
  a : 10;
} {
  b : 20;
} {
  c : 30;
};
obj.a, obj.b, obj.c, obj.d
```

```bash:
$ fl7 'obj : {a : 10} {b : 20} {c : 30}; obj.a, obj.b, obj.c, obj.d'
10
20
30
NULL
```

### `key`は識別子以外であってもよい

`key`が識別子以外であった場合、それを評価した文字列でメンバアクセスを行う。

```perl:
obj : {
  aaa : 777;
  10 : 888;
};
key : "aaa";
obj.("a" * 3),
obj.(key),
obj.10,
```

```bash:
$ fl7 'obj : {aaa : 777; 10 : 888}; key : "aaa"; obj.("a" * 3), obj.(key), obj.10,'
777
777
888
```

## プロパティアクセス

プロパティアクセス演算子`object[key]`は、`object`のプロパティ`key`を取得する**後置演算子**である。
ここで、`key`は文字列の値を表す。
結合優先度はその他の後置演算子と等しい。

プロパティアクセスでは親オブジェクトを無視する。

それ以外の挙動はメンバアクセスと概ね等しい。

```perl:
obj : {
  a : 10;
} {
  b : 20;
} {
  c : 30;
};
obj["a"], obj["b"], obj["c"], obj["d"]
```

```bash:
$ fl7 'obj : {a : 10} {b : 20} {c : 30}; obj["a"], obj["b"], obj["c"], obj["d"]'
NULL
NULL
30
NULL
```

## メソッドアクセス

[メソッドアクセス演算子](https://qiita.com/MirrgieRiana/items/bd41a1604c60b85e2d1e#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9)`object::method`は、メンバアクセス演算子と左部分適用演算子の複合である`object.method << object`と等価な動きをする。
メソッドアクセス演算子を使うとオブジェクト指向におけるMethodの動きが再現できる。

## メソッド風左バインド

[メソッド風左バインド](https://qiita.com/MirrgieRiana/items/bd41a1604c60b85e2d1e#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E9%A2%A8%E5%B7%A6%E3%83%90%E3%82%A4%E3%83%B3%E3%83%89)を使うと、メンバでない関数をメソッドのように呼び出すことができる。

# エントリーの列挙

ストリーマ展開演算子`object[]`をオブジェクトに対して用いると、`object`が持っているすべてのプロパティを`{key = key; value = value}`という形の要素からなるストリーマで取得する。

```perl:
{
  a : 10;
  b : 20;
  c : 30;
}[]
```

```bash:
$ fl7 '{a : 10; b : 20; c : 30}[]'
{key:a;value:10}
{key:b;value:20}
{key:c;value:30}
```

# 改変

## プロパティ代入

メンバアクセス演算子`object.key`およびプロパティアクセス演算子`object[key]`は、セットコンテキストで記述されるとオブジェクトのプロパティに代入を行う。

```perl:
parent :        {a : 10; b: 20};
object : parent {             };

object.a = 100;
object["b"] = 200;
object.c = 300;
object["d"] = 400;

parent, object
```

```bash:
$ fl7 'parent : {a : 10; b: 20}; object : parent {}; object.a = 100; object["b"] = 200; object.c = 300; object["d"] = 400; parent, object'
{a:10;b:20}
{a:100;b:200;c:300;d:400}
```

親オブジェクトのプロパティがメンバとして見えている状態でも、代入時には子オブジェクト側が改変される。

## 多重プロパティ代入

多重プロパティ代入演算子`object.{entries}`は、`entries`が示すエントリー列を`object`に注入したうえで`object`を返す後置演算子である。

この演算子では`object`が改変を受ける。

```perl:
parent :        {a : 10};
object : parent {      };

result : object.{
  a : 100;
  b : 200;
};

parent,
object,
result,
result === object,
```

```bash:
$ fl7 'parent : {a : 10}; object : parent {}; result : object.{a : 100; b : 200}; parent, object, result, result === object'
{a:10}
{a:100;b:200}
{a:100;b:200}
TRUE
```

**Tips** 多重プロパティ代入は親クラスのコンストラクタから返却されたオブジェクトに、子クラスのコンストラクタ内で続きの初期化を行う際に便利である。

```perl:
Parent : {
  new : _ -> {
    a : 10;
  };
};
Child : Parent {
  new : _ -> ^.new(_).{
    b : 20;
  };
};

Child::new()
```

```bash:
$ fl7 'Parent : { new : _ -> { a : 10; }; }; Child : Parent { new : _ -> ^.new(_).{ b : 20; }; }; Child::new()'
{a:10;b:20}
```