aboutsummaryrefslogtreecommitdiffhomepage
path: root/slide.saty
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2024-01-22 00:03:22 +0900
committernsfisis <nsfisis@gmail.com>2024-01-22 01:58:02 +0900
commit7f227daa9f1afc73ebd7ace969391acedceed06a (patch)
tree6034873614cc5e900f6ac302375f95bc2afe534a /slide.saty
parent89c4d83277dbc6d317c981e4959f452052bb40e7 (diff)
downloadphpstudy-160-slides-7f227daa9f1afc73ebd7ace969391acedceed06a.tar.gz
phpstudy-160-slides-7f227daa9f1afc73ebd7ace969391acedceed06a.tar.zst
phpstudy-160-slides-7f227daa9f1afc73ebd7ace969391acedceed06a.zip
Write slides
Diffstat (limited to 'slide.saty')
-rw-r--r--slide.saty268
1 files changed, 263 insertions, 5 deletions
diff --git a/slide.saty b/slide.saty
index dbed3ff..4423e21 100644
--- a/slide.saty
+++ b/slide.saty
@@ -1,3 +1,4 @@
+@require: option
@require: class-slydifi/theme/akasaka
@require: code-printer/code-design
@require: code-printer/code-printer
@@ -13,6 +14,10 @@ let-block +code-block-php source =
)(source);
>
+let big-textbox ?:size-opt it =
+ let size = Option.from 32pt size-opt in
+ FigBox.textbox?:(set-font-size size) it
+
open FigBox
in
@@ -24,10 +29,12 @@ document '<
+make-title(|
title = {
- |TODO
+ |PHPStanの力で
+ |Algebraic Data Typesを
+ |実現する
|};
author = {|nsfisis (いまむら)|};
- date = {|第TODO回PHP勉強会@東京|};
+ date = {|第160回PHP勉強会@東京|};
|);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -36,19 +43,270 @@ document '<
+fig-center(vconcat [
gap 75pt;
hconcat [
- textbox{nsfisis (いまむら)};
+ big-textbox{nsfisis (いまむら)};
gap 20pt;
include-image 50pt `assets/me.jpeg`;
];
gap 20pt;
- textbox{\@ デジタルサーカス株式会社};
+ big-textbox{\@ デジタルサーカス株式会社};
+ ]);
+ >
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ +frame{ADTとは}<
+ +fig-center(vconcat [
+ gap 75pt;
+ big-textbox?:(48pt){Algebraic Data Type};
+ gap 20pt;
+ big-textbox?:(48pt){代数的データ型};
+ ]);
+ >
+
+ +frame{ADTとは}<
+ +fig-center(vconcat [
+ gap 75pt;
+ big-textbox?:(48pt){すごい enum 型};
+ ]);
+ >
+
+ +frame{ADTの例}<
+ +code-block-php(`// 注: これは架空の文法です
+enum JsonValue {
+ case Null;
+ case Boolean(bool);
+ case Number(int|float);
+ case String(string);
+ case Array(array);
+ case Object(array);
+}
+`);
+ >
+
+ +frame{ADTの例(今回扱うもの)}<
+ +code-block-php(`// 注: これは架空の文法です
+enum OptionalInt {
+ case None;
+ case Some(int);
+}
+`);
+ >
+
+ +frame{実現したいもの}<
+ +code-block-php(`/** @return TODO */
+function f(): TODO {
+ return /* Some(int) か None を返す */;
+}
+
+$x = f();
+if (/* $x が Some かどうか判定する */) {
+ echo /* $x が内部に持っている int の値を「型安全に」取り出す */;
+}
+`);
+ >
+
+ +frame{どうやって実現するか?}<
+ +fig-center(vconcat [
+ gap 40pt;
+ big-textbox{Array shape 編};
+ gap 20pt;
+ big-textbox{Object shape 編};
+ gap 20pt;
+ big-textbox{Type assertion 編};
+ gap 20pt;
+ big-textbox{PHPStan 魔改造編};
+ ]);
+ >
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ +frame{Array shape}<
+ +code-block-php(`$none = [
+ 'has_value' => false,
+];
+$some = [
+ 'has_value' => true,
+ 'value' => 42,
+];`);
+ >
+
+ +frame{Array shape}<
+ +code-block-php(`/** @var array{has_value: false} $none */
+$none = [
+ 'has_value' => false,
+];
+/** @var array{has_value: true, value: int} $some */
+$some = [
+ 'has_value' => true,
+ 'value' => 42,
+];`);
+ >
+
+ +frame{Array shape}<
+ +code-block-php(`/** @return (array{has_value: false}
+ |array{has_value: true, value: int}) */
+function f(): array { /* 略 */ }`);
+ >
+
+ +frame{Array shape}<
+ +code-block-php(`/** @return (array{has_value: false}
+ |array{has_value: true, value: int}) */
+function f(): array { /* 略 */ }
+
+$x = f();
+if ($x['has_value']) {
+ // PHPStan は、$x['value'] が int であることを認識できる
+ echo $x['value'];
+}
+`);
+ >
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ +frame{Object shape}<
+ +code-block-php(`/** @var object{has_value: false} $none */
+$none = (object)[
+ 'has_value' => false,
+];
+/** @var object{has_value: true, value: int} $some */
+$some = (object)[
+ 'has_value' => true,
+ 'value' => 42,
+];`);
+ >
+
+ +frame{Object shape}<
+ +code-block-php(`/** @return (object{has_value: false}
+ |object{has_value: true, value: int}) */
+function f(): stdClass { /* 略 */; }
+
+$x = f();
+if ($x->has_value) {
+ // 型が絞りこまれない!
+ echo $x->value;
+}
+`);
+ >
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ +frame{Type assertion}<
+ +code-block-php(`/** @phpstan-assert-if-true int $a */
+/** @phpstan-assert-if-false !int $a */
+function check_is_int(mixed $a): bool {
+ return is_int($a);
+}
+
+$a = hogehoge();
+if (check_is_int($a)) {
+ PHPStan\dumpType($a);
+ // => int
+}
+`);
+ >
+
+ +frame{Type assertion}<
+ +code-block-php(`abstract class OptionalInt {
+ /**
+ * @phpstan-assert-if-true OptionalIntSome $this
+ * @phpstan-assert-if-false OptionalIntNone $this
+ */
+ public function hasValue(): bool {
+ return $this instanceof OptionalIntSome;
+ }
+}
+class OptionalIntNone extends OptionalInt {}
+class OptionalIntSome extends OptionalInt {
+ public function __construct(public int $value) {}
+}
+`);
+ >
+
+ +frame{Type assertion}<
+ +code-block-php(`$x = f();
+if ($x->hasValue()) {
+ echo $x->value;
+}
+`);
+ >
+
+ +frame{Type assertion}<
+ +code-block-php(`abstract class OptionalInt {
+ /**
+ * @phpstan-assert-if-true OptionalIntSome $this
+ * @phpstan-assert-if-false OptionalIntNone $this
+ */
+ public function hasValue(): bool {
+ return $this instanceof OptionalIntSome;
+ }
+}
+class OptionalIntNone extends OptionalInt {}
+class OptionalIntSome extends OptionalInt {
+ public function __construct(public int $value) {}
+}
+`);
+ >
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ +frame{ここまでのまとめ}<
+ +fig-center(vconcat [
+ gap 75pt;
+ big-textbox{Array ではうまく動く};
+ gap 20pt;
+ big-textbox{Object では動かない / 課題アリ};
+ gap 20pt;
+ big-textbox{Object でも型を絞り込みたい};
+ ]);
+ >
+
+ +frame{PHPStan 魔改造}<
+ +code-block-php(`if ($x['has_value']) {
+ // $x['has_value'] の型が true に確定したことで、
+ // $x に HasOffsetValueType('has_value', true) の型が付く
+ echo $x['value'];
+}
+`);
+ >
+
+ +frame{PHPStan 魔改造}<
+ +code-block-php(`if ($x['has_value']) {
+ // $x['has_value'] の型が true に確定したことで、
+ // $x に HasOffsetValueType('has_value', true) の型が付く
+ echo $x['value'];
+}
+`);
+ +p{}
+ +code-block-php(`// (array{has_value: false}
+// |array{has_value: true, value: int})
+// と
+// HasOffsetValueType('has_value', true)
+// とを合成する
+`);
+ >
+
+ +frame{PHPStan 魔改造}<
+ +fig-center(vconcat [
+ gap 40pt;
+ big-textbox?:(24pt){HasPropertyValueType};
+ gap 20pt;
+ big-textbox?:(24pt){HasPropertyValueType と object shape の合成};
+ gap 20pt;
+ big-textbox?:(24pt){内から外への型の伝播};
]);
>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+frame{まとめ}<
- +p{TODO}
+ +fig-center(vconcat [
+ gap 75pt;
+ big-textbox?:(24pt){PHPStan と array shape を使った ADT もどき};
+ gap 20pt;
+ big-textbox?:(24pt){Object の型の絞り込みは未実装};
+ gap 20pt;
+ big-textbox?:(24pt){現在鋭意実装中};
+ ]);
>
>