Skip to content

ジェネリクスを自分のものにする

公開日:

オンラインZoomウェビナーで開催された『Qiita Night~PHP~』でライトニングトーク(10分)として発表しました。

Download PDF

スライドテキスト

Page 1

ジェネリクスを

自分のものにする

Getting Started with Generic Programming

pixiv Inc.

USAMI Kenta

2022-01-25 Qiita Night ~PHP~

Page 2

お前誰よ

  • うさみけんた (@tadsan) / Zonu.EXE / にゃんだーすわん
  • ピクシブ株式会社 pixiv事業本部 エンジニア
  • 最近はピクシブ百科事典(dic.pixiv.net)を開発しています
  • すごい肩書の発表者が多いですが僕はヒラのPHPerです (IC?)
  • Emacs Lisper, PHPer
  • Emacs PHP Modeを開発しています (2017年-)

Page 3

Page 4

Page 5

静的解析してますか?

Page 6

Page 7

Page 8

ジェネリクスの話をしよう

Page 9

ジェネリックプログラミング - Wikipedia(2022-05-02T17:30:09の版)より引用

Page 10

データ型に依存しない…?

Page 11

抽象的かつ汎用的…?

Page 12

Page 13

まず基本的な静的型の話

Page 14

静的型付け (statically typing)

  • 静的 = 実行しなくても型がわかる状態 (反対語は動的 = プログラム実行)
  • 型宣言 (コードに型を書く)
  • 現状のPHPではパラメータとプロパティに型宣言できる
  • PHPは処理系が実行時の型を保障してくれる (口約束ではない!)
  • 型推論 (文字で型を書かなくても空気を読んでくれる)
  • $a = count($array) のとき $a の型は常に int
  • $b = 1 + 2 の結果は int(3)

Page 15

型付けの具体例を見ていきましょう

Page 16

まずはジェネリクスとあまり関係ない例

Page 17

型がついていない関数(PHP5)

型宣言なし

= mixed

function add($a, $b) {return $a + $b;}

Page 18

スカラー型宣言(PHP7)

int + intって本当にintなの?

function add(int $a, int $b): int {return $a + $b;}

Page 19

広い値をとるにはfloatが必要

ひとつの解決策ではあるが… 不必要にfloatを強制するのか

function add(float $a, float $b): float {return $a + $b;}

Page 20

PHPDocの型注釈(アノテーション)

あえて型宣言を省略する

/*** @param int|float $a* @param int|float $b* @return int|float*/function add($a, $b) {return $a + $b;}

Page 21

ユニオン型宣言 (PHP8.0)

function add(int|float $a, int|float $b): int|float {return $a + $b;}

Page 22

基本的な型宣言

  • PHPの言語として記述できる静的な型宣言は大まかに3種類
  • 具体的な型
  • int, float, string, bool, array, DateTimeInterface, ...
  • 複合的な型
  • A|B, int|false, ?DateTime, ArrayAccess&Traversable ...
  • 型定義をしない (または mixed)

Page 23

かくして世界は型の炎に包まれた

Page 24

だが型なしは死滅していなかった

Page 25

別の例を考えてみましょう

Page 26

配列の先頭要素を返す関数

function first(array $a, mixed $default): mixed {return $a[0] ?? $default;}

$vの型は何?

$v = first([0, 1, 2], 'default');

Page 27

int専用関数をつくろう

function first_int(array $a): ?int {return $a[0] ?? null;}

$v = first_int([0, 1, 2]) ?? 'default';

Page 28

string専用関数をつくろう

function first_string(array $a): ?string return $a[0] ?? null;}

$v = first_string(['a', 'b', 'c']) ?? 'default';

Page 29

DateTime専用関数をつくろう

function first_DateTime(array $a): ?DateTime return $a[0] ?? null;}

$v = first_DataTime([]) ?? 'default';

Page 30

関数量産はめんどくさい

Page 31

ユニオン型は問題解決しない

なんでDateTimeとか

function first(array $a): int|string|DateTime|null {return $a[0] ?? $default;}

出てくるの?

$v = first([0, 1, 2], 'default');

Page 32

?DateTimeとかint|falseくらいなら問題ない

Page 33

それを補うのがPHPDoc

Page 34

PHPDocタグいくついえるかな

Page 35

@param

Page 36

@return

Page 37

@var

Page 38

@template

Page 39

…なにそれ?

Page 40

@template は型変数

/*** @template TValue* @template TDefault* @param array<TValue> $a* @param TDefault $default* @return TValue|TDefault*/function first(array $a, mixed $default) {

Page 41

Page 42

Page 43

やっていきましょう

Page 44

Page 45

Page 46

いまは内部型表現の制約でシンプルな型しか付かないが

Page 47

そのうち万病に効くようになる

Page 48

Page 49

Page 50

テンプレートはクラスにも書ける

Page 51

Page 52

ジェネレータでも使える

Page 53

Page 54

コレクションも簡単に定義できる

Page 55

Page 56

tagged union

Page 57

Page 58

モナド

Page 59

Page 60

実際難しいことは(そんなに)やってない

Page 61

試行錯誤したことがない技術が難しいのはあたりまえ

Page 62

PHPStanはWebでこねこね試行錯誤がやりやすい

Page 63

PHPStan完全にマスターしよう

Page 64

Page 65

今回はひたすら実例を詰め込んで話しました

Page 66

ジェネリクスの知識はなくてもPHPStanは使える

Page 67

Page 68

宣伝

Page 69

Page 70

Page 71

会社で実用しようPHPStan