DNF型
英語: DNF Types / Disjunctive Normal Form Types 日本語: DNF型 / 選言標準形型 (せんげん ひょうじゅんけい がた) 型宣言記法
従来の型宣言は全て有効
A
string
交叉型を()で括ったもの
(A&B)
ユニオン型と交叉型を組み合せた型
(A&B)|D
C|(X&D)|null
(A&B&D)|int|null
一方で以下のような型宣言は無効であり、許可されない。
論理的に同じ内容を含む、冗長な|
(A&B)|(A&B) … まったく同じ
(A&B)|(B&A) … 順番を入れ替えただけ
(A&B)|A … 両端にAが含まれる
()で括られていない&
A|B&C
A|(B&C) としなければいけない
|の外側にある&
A&(B|C) … DNF標準形ではない
(A&B)|(A&C)に書き換え可能
用語
数学用語が混じってわかりにくいが、下記の表のように対応する。
disjunction = 選言 = 論理和 = OR ≒ ユニオン型(union) | conjunction = 連言 = 論理積 = AND ≒ 交叉型(intersection) & 構造
DNF型は下記のような構造を持つ。「→」は「からなる」、→の左側が複数あるものは「そのうちのどれか」のように読むとよい。
DNF型 → 論理和
論理和 → 論理積
論理和 → 論理和|論理積
論理積 → リテラル
論理積 → (論理積&リテラル)
NOT に相当する演算子は実装されておらず、補集合は表現できない。
クラスの拡張
戻り値の共変性
クラスの拡張においては、戻り値の型宣言は共変である必要があるため|の削除と&の追加が許可される。 RFCでは以下のようなコード例が紹介されている。
code:php
interface ITest {
public function stuff(): (A&B)|D;
}
// A&B は (A&B)|D より狭い型なので許容される
class TestOne implements ITest {
public function stuff(): (A&B) {}
}
// D は (A&B)|D の部分集合なので許容される
class TestTwo implements ITest {
public function stuff(): D {}
}
// C が A&B の部分集合なら、許容される
class TestThree implements ITest {
public function stuff(): C|D {}
}
// (A&B)|D から &B を削るとと B を実装していないクラスが受け入れられ、
// インターフェイスよりも型が拡がってしまうため許容されない
class TestFour implements ITest {
public function stuff(): A|D {}
}
interface ITestTwo {
public function things(): C|D {}
}
// ITest と同じ (A&B)|D だが、 ITestTwo の C|D より拡がってしまい
// C を実装していないクラスができてしまうので許容されない
class TestFive implements ITestTwo {
public function things(): (A&B)|D {}
}
パラメータの反変性
クラスの拡張においては、パラメータは反変である必要があるため|の追加と&の削除が許可される。 RFCでは以下のようなコード例が紹介されている。
code:php
interface ITest {
public function stuff((A&B)|D $arg): void {}
}
// (A&B)|D はすべて受け入れた上で Z を追加するのは有効なので許容される
class TestOne implements ITest {
public function stuff((A&B)|D|Z $arg): void {}
}
// A は A&B のスーパーセットであり A だけを実装したクラスを受け入れるので許容される
class TestOne implements ITest {
public function stuff(A|D $arg): void {}
}
// インターフェイスでは D を受け入れ可能だが、削られているため許容されない
class TestOne implements ITest {
public function stuff((A&B) $arg): void {}
}
interface ITestTwo {
public function things(C|D $arg): void;
}
// C を継承するものは A と B を実装しているので、許容される
class TestFive implements ITestTwo {
public function things((A&B)|D $arg): void;
}