基礎
{
}
でオブジェクトが作れる。
{
key : "value";
}
オブジェクトの値はobject.key
で得られる。
{
key : "value";
}.key
$ fl7 '{key : "value"}.key'
value
オブジェクトに{
}
を後置すると継承したオブジェクトが作れる。
object.key
では親の値も見える。
parent : {
key : "value";
};
obj : parent {
key2 : "value2";
};
obj.key, obj.key2
$ fl7 'parent : {key : "value"}; obj : parent {key2 : "value2"}; obj.key, obj.key2'
value
value2
これを利用すると「クラス」が作れる。
object::method(args)
で、object.method(object; args)
の動きをする。
Obj : {
get_value : this -> this.value;
};
obj : Obj {
value : "value!!!";
};
obj::get_value()
$ fl7 'Obj : {get_value : this -> this.value}; obj : Obj {value : "value!!!"}; obj::get_value()'
value!!!
オブジェクト生成
オブジェクト生成演算子
オブジェクト生成演算子{entries}
は、オブジェクトを生成する括弧類である。
entries
には;
区切りで0個以上のエントリーを記述する。
宣言エントリー
key : value
はkey
という名前を宣言しつつキーにvalue
を代入するエントリーである。
key
は宣言されたオブジェクト内のどこからでも参照できる。
{
x : y - 1;
y : 100;
z : y + 1;
}
$ fl7 '{x : y - 1; y : 100; z : y + 1}'
{x:99;y:100;z:101}
これを使って循環参照を行うこともできる。
obj : {
f : n -> n === 0 ? 1 : f(n - 1) * n;
};
obj.f(5)
$ fl7 'obj : {f : n -> n === 0 ? 1 : f(n - 1) * n}; obj.f(5)'
120
key
は数値・文字列リテラル・括弧であってもよい。
その場合、文字列に変換されてキーとして認識される。
{
E : "identifier";
100 : "number";
"#" : "string";
(PI) : "formula";
}
$ fl7 '{E : "identifier"; 100 : "number"; "#" : "string"; (PI) : "formula"}'
{100:number;E:identifier;#:string;3.141592653589793:formula}
代入エントリー
key = value
はkey
というキーにvalue
を代入するエントリーである。
x : 20;
{
x = x;
}
$ fl7 'x : 20; {x = x}'
{x:20}
代入エントリーでは名前を宣言しないため、意図せず循環参照に陥ることがない。
その他のことは概ね宣言エントリーと同様である。
即席代入エントリー
key
はkey
というキーにkey
を代入するエントリーである。
x : 20;
{
x;
}
$ fl7 'x : 20; {x}'
{x:20}
その他のことは概ね代入エントリーと同様である。
Tips 即席代入はコンストラクタメソッド内でよく使われる。
Obj : {
new : class, value -> class {
value;
};
get_value : this -> this.value;
};
Obj::new("Value!")::get_value()
$ fl7 'Obj : {new : class, value -> class {value}; get_value : this -> this.value}; Obj::new("Value!!!")::get_value()'
Value!!!
継承つきオブジェクト生成演算子
継承つきオブジェクト生成演算子parent {entries}
は、parent
を継承したオブジェクトを生成する後置演算子である。
entries
は、オブジェクト生成演算子{entries}
と同じ仕様を持つ。
継承つきオブジェクト生成演算子の結合優先度はその他の後置演算子と等しい。
その他の挙動は、概ねオブジェクト生成演算子と等しい。
親オブジェクト参照子
親オブジェクト参照子^
は、オブジェクト内でその親オブジェクトを参照できる式である。
parent : {
a : 10;
};
obj : parent {
a : 20;
c : ^.a;
};
obj.c
$ fl7 'parent : {a : 10}; obj : parent {a : 20; c : ^.a}; obj.c'
10
クラスの表現ができる
継承つきオブジェクト生成演算子は、オブジェクト指向におけるClass-Object間の関係を表すのに使うことができる。
Obj : {
new : class, value -> class {
value;
};
get_value : this -> this.value;
};
Obj::new("Value!!!")::get_value()
$ fl7 'Obj : {new : class, value -> class {value}; get_value : this -> this.value}; Obj::new("Value!!!")::get_value()'
Value!!!
クラスの継承の表現ができる
継承つきオブジェクト生成演算子は、オブジェクト指向におけるSuper Class-Sub Class間の関係を表すのに使うことができる。
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()
$ 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()
は次のように等価に変形できる。
次のコードは変形前の状態である。
SubObj::new("Value!!!")::get_value()
SubObj::new("Value!!!")
は、SubObj.new(SubObj; "Value!!!")
に変形できる。
SubObj.new(SubObj; "Value!!!")::get_value()
SubObj.new(SubObj; "Value!!!")
を展開する。
Obj.new(SubObj; "Value!!!")::get_value()
Obj.new(SubObj; "Value!!!")
を展開する。
obj : SubObj {
value = "Value!!!";
};
obj::get_value()
obj::get_value()
を展開する。
obj : SubObj {
value = "Value!!!";
};
obj.get_value(obj)
SubObj
の子obj
自身にはプロパティget_value
が存在しないため、一つ上の親であるSubObj
が持つプロパティget_value
がヒットする。
obj.get_value
はSubObj.get_value
で置換できる。
obj : SubObj {
value = "Value!!!";
};
SubObj.get_value(obj)
SubObj.get_value
を展開する。
obj : SubObj {
value = "Value!!!";
};
Obj.get_value(obj) * 2
Obj.get_value
を展開する。
obj : SubObj {
value = "Value!!!";
};
obj.value * 2
obj
はプロパティvalue
を持っており、その値はここでは"Value!!!"
である。
これをobj.value
に代入する。
"Value!!!" * 2
"Value!!!" * 2
を計算する。
"Value!!!Value!!!"
fl7におけるオブジェクトの継承はこのようにして成り立っている。
エントリーストリームからの生成
エントリーストリームからのオブジェクト生成演算子{[entries]}
は、entries
をエントリーのストリームと解釈し、それらで構成されるオブジェクトを生成する括弧類である。
{[
1 .. 5 | {
key : _;
value : _ * 11;
}
]}
$ fl7 '{[1 .. 5 | {key : _; value : _ * 11}]}'
{1:11;2:22;3:33;4:44;5:55}
アクセス
メンバアクセス
メンバアクセス演算子object.key
は、object
のメンバkey
を取得する後置演算子である。
ここでkey
は識別子の名前そのものを表す。
結合優先度はその他の後置演算子と等しい。
メンバアクセスではキーが存在しなかった場合、親オブジェクトを再帰的に遡ってヒットするキーを検索する。
全くヒットしなかった場合、NULL
が返る。
obj : {
a : 10;
} {
b : 20;
} {
c : 30;
};
obj.a, obj.b, obj.c, obj.d
$ fl7 'obj : {a : 10} {b : 20} {c : 30}; obj.a, obj.b, obj.c, obj.d'
10
20
30
NULL
key
は識別子以外であってもよい
key
が識別子以外であった場合、それを評価した文字列でメンバアクセスを行う。
obj : {
aaa : 777;
10 : 888;
};
key : "aaa";
obj.("a" * 3),
obj.(key),
obj.10,
$ fl7 'obj : {aaa : 777; 10 : 888}; key : "aaa"; obj.("a" * 3), obj.(key), obj.10,'
777
777
888
プロパティアクセス
プロパティアクセス演算子object[key]
は、object
のプロパティkey
を取得する後置演算子である。
ここで、key
は文字列の値を表す。
結合優先度はその他の後置演算子と等しい。
プロパティアクセスでは親オブジェクトを無視する。
それ以外の挙動はメンバアクセスと概ね等しい。
obj : {
a : 10;
} {
b : 20;
} {
c : 30;
};
obj["a"], obj["b"], obj["c"], obj["d"]
$ fl7 'obj : {a : 10} {b : 20} {c : 30}; obj["a"], obj["b"], obj["c"], obj["d"]'
NULL
NULL
30
NULL
メソッドアクセス
メソッドアクセス演算子object::method
は、メンバアクセス演算子と左部分適用演算子の複合であるobject.method << object
と等価な動きをする。
メソッドアクセス演算子を使うとオブジェクト指向におけるMethodの動きが再現できる。
メソッド風左バインド
メソッド風左バインドを使うと、メンバでない関数をメソッドのように呼び出すことができる。
エントリーの列挙
ストリーマ展開演算子object[]
をオブジェクトに対して用いると、object
が持っているすべてのプロパティを{key = key; value = value}
という形の要素からなるストリーマで取得する。
{
a : 10;
b : 20;
c : 30;
}[]
$ fl7 '{a : 10; b : 20; c : 30}[]'
{key:a;value:10}
{key:b;value:20}
{key:c;value:30}
改変
プロパティ代入
メンバアクセス演算子object.key
およびプロパティアクセス演算子object[key]
は、セットコンテキストで記述されるとオブジェクトのプロパティに代入を行う。
parent : {a : 10; b: 20};
object : parent { };
object.a = 100;
object["b"] = 200;
object.c = 300;
object["d"] = 400;
parent, object
$ 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
が改変を受ける。
parent : {a : 10};
object : parent { };
result : object.{
a : 100;
b : 200;
};
parent,
object,
result,
result === object,
$ 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 多重プロパティ代入は親クラスのコンストラクタから返却されたオブジェクトに、子クラスのコンストラクタ内で続きの初期化を行う際に便利である。
Parent : {
new : _ -> {
a : 10;
};
};
Child : Parent {
new : _ -> ^.new(_).{
b : 20;
};
};
Child::new()
$ fl7 'Parent : { new : _ -> { a : 10; }; }; Child : Parent { new : _ -> ^.new(_).{ b : 20; }; }; Child::new()'
{a:10;b:20}