From 182d44d2f3c714dc3d51e582093a742e04532714 Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Wed, 19 Aug 2026 15:34:21 +0200 Subject: [PATCH] feat(presentation): init --- justfile | 11 +- presentation/figs/HES-SO_logo_CMJN.svg | 133 +++ presentation/figs/hei_logo.svg | 99 ++ presentation/figs/isc_logo.svg | 988 ++++++++++++++++++ presentation/figs/midas_output.txt | 5 + presentation/figs/midas_output2.txt | 33 + presentation/figs/mypy_logo.svg | 99 ++ presentation/figs/pyright-logo.png | Bin 0 -> 25250 bytes presentation/figs/python-logo.svg | 243 +++++ presentation/figs/script.py | 2 + presentation/presentation.typ | 49 + presentation/requirements.typ | 3 + presentation/sections/01_introduction.typ | 85 ++ presentation/sections/02_state_of_the_art.typ | 32 + presentation/sections/03_midas.typ | 402 +++++++ .../sections/04_status_and_future.typ | 4 + 16 files changed, 2187 insertions(+), 1 deletion(-) create mode 100644 presentation/figs/HES-SO_logo_CMJN.svg create mode 100644 presentation/figs/hei_logo.svg create mode 100644 presentation/figs/isc_logo.svg create mode 100644 presentation/figs/midas_output.txt create mode 100644 presentation/figs/midas_output2.txt create mode 100644 presentation/figs/mypy_logo.svg create mode 100644 presentation/figs/pyright-logo.png create mode 100644 presentation/figs/python-logo.svg create mode 100644 presentation/figs/script.py create mode 100644 presentation/presentation.typ create mode 100644 presentation/requirements.typ create mode 100644 presentation/sections/01_introduction.typ create mode 100644 presentation/sections/02_state_of_the_art.typ create mode 100644 presentation/sections/03_midas.typ create mode 100644 presentation/sections/04_status_and_future.typ diff --git a/justfile b/justfile index 55ddc87..9cb3878 100644 --- a/justfile +++ b/justfile @@ -7,6 +7,8 @@ outdir := "out" report_out_path := outdir / "report.pdf" summary_out_path := outdir / "summary.pdf" poster_out_path := outdir / "poster.pdf" +presentation_out_path := outdir / "presentation.pdf" +presentation_pdfpc_path := outdir / "presentation.pdfpc" default: all @@ -39,4 +41,11 @@ summary: _setup poster: _setup typst c --root . poster/poster.typ {{poster_out_path}} -all: report summary poster \ No newline at end of file +presentation: _setup + typst c --root . presentation/presentation.typ {{presentation_out_path}} + typst eval --root . 'query().first().value' --in presentation/presentation.typ > {{presentation_pdfpc_path}} + +present: presentation + pympress {{presentation_out_path}} + +all: report summary poster presentation \ No newline at end of file diff --git a/presentation/figs/HES-SO_logo_CMJN.svg b/presentation/figs/HES-SO_logo_CMJN.svg new file mode 100644 index 0000000..09eee69 --- /dev/null +++ b/presentation/figs/HES-SO_logo_CMJN.svg @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/figs/hei_logo.svg b/presentation/figs/hei_logo.svg new file mode 100644 index 0000000..d94f8f6 --- /dev/null +++ b/presentation/figs/hei_logo.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/figs/isc_logo.svg b/presentation/figs/isc_logo.svg new file mode 100644 index 0000000..deb0239 --- /dev/null +++ b/presentation/figs/isc_logo.svg @@ -0,0 +1,988 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/figs/midas_output.txt b/presentation/figs/midas_output.txt new file mode 100644 index 0000000..3e16728 --- /dev/null +++ b/presentation/figs/midas_output.txt @@ -0,0 +1,5 @@ +Error in /tmp/script.py from L2:12 to L2:15: + fib.append(3.0) + ~~~> Wrong type for argument 'object', expected int, got float + +Errors: 1 diff --git a/presentation/figs/midas_output2.txt b/presentation/figs/midas_output2.txt new file mode 100644 index 0000000..2790607 --- /dev/null +++ b/presentation/figs/midas_output2.txt @@ -0,0 +1,33 @@ +Info in /tmp/script.py from L1:8 to L1:9: + fib = [1,1,2] + ~> Type: int + +Info in /tmp/script.py from L1:10 to L1:11: + fib = [1,1,2] + ~> Type: int + +Info in /tmp/script.py from L1:12 to L1:13: + fib = [1,1,2] + ~> Type: int + +Info in /tmp/script.py from L1:7 to L1:14: + fib = [1,1,2] + ~~~~~~~> Type: list[int] + +Info in /tmp/script.py from L2:12 to L2:15: + fib.append(3.0) + ~~~> Type: float + +Info in /tmp/script.py from L2:1 to L2:4: + fib.append(3.0) + ~~~> Type: list[int] + +Info in /tmp/script.py from L2:1 to L2:16: + fib.append(3.0) + ~~~~~~~~~~~~~~~> Type:  + +Error in /tmp/script.py from L2:12 to L2:15: + fib.append(3.0) + ~~~> Wrong type for argument 'object', expected int, got float + +Errors: 1 Infos: 7 diff --git a/presentation/figs/mypy_logo.svg b/presentation/figs/mypy_logo.svg new file mode 100644 index 0000000..4eaf65d --- /dev/null +++ b/presentation/figs/mypy_logo.svg @@ -0,0 +1,99 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/presentation/figs/pyright-logo.png b/presentation/figs/pyright-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5a0686629ed89ba6de2221cbb775dd6e5dcdb2fa GIT binary patch literal 25250 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�rzK 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�MvI$^P5q%Y7$ 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 xKnLQZ`+&9jzkaR&KQ3q%O#H9W|8H + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/figs/script.py b/presentation/figs/script.py new file mode 100644 index 0000000..ac2f2d9 --- /dev/null +++ b/presentation/figs/script.py @@ -0,0 +1,2 @@ +fib = [1, 1, 2] +fib.append(3.0) \ No newline at end of file diff --git a/presentation/presentation.typ b/presentation/presentation.typ new file mode 100644 index 0000000..ba7a851 --- /dev/null +++ b/presentation/presentation.typ @@ -0,0 +1,49 @@ +#import "requirements.typ": * +#import "../meta.typ" +#import codly: codly-init, codly +#import "@preview/codly-languages:0.1.10": codly-languages + +#pdfpc.config( + start-time: datetime(hour: 13, minute: 15, second: 0), + end-time: datetime(hour: 13, minute: 55, second: 0), + duration-minutes: 20, +) + +#show raw.where(block: true): set text(size: 0.8em) +#show: codly-init.with() +#codly( + languages: codly-languages, +) + +#show: unistra-theme.with( + config-info( + author: meta.authors, + institution: meta.school, + date: datetime(year: 2026, month: 8, day: 24).display("[day].[month].[year]"), + title: [Midas], + subtitle: [Hybrid Type Checking for Python], + //title: meta.title, + //subtitle: meta.subtitle, + //short-title: [Midas], + //short-subtitle: [Hybrid Type Checking for Python], + logo: image("figs/isc_logo.svg"), + ), + config-common( + enable-pdfpc: true, + ), + config-store( + font: "Source Sans Pro" + ) +) + +#title-slide( + logos: ( + box(inset: (x: 0.8em), image("figs/hei_logo.svg", height: 2cm)), + box(inset: (x: 0.8em), image("figs/isc_logo.svg", height: 2cm)), + ) +) + +#include "sections/01_introduction.typ" +#include "sections/02_state_of_the_art.typ" +#include "sections/03_midas.typ" +#include "sections/04_status_and_future.typ" diff --git a/presentation/requirements.typ b/presentation/requirements.typ new file mode 100644 index 0000000..45f8a0f --- /dev/null +++ b/presentation/requirements.typ @@ -0,0 +1,3 @@ +#import "@preview/touying:0.7.4": * +#import "@preview/touying-unistra-pristine:1.4.3": * +#import "@local/codly:1.3.1" \ No newline at end of file diff --git a/presentation/sections/01_introduction.typ b/presentation/sections/01_introduction.typ new file mode 100644 index 0000000..453194d --- /dev/null +++ b/presentation/sections/01_introduction.typ @@ -0,0 +1,85 @@ +#import "../requirements.typ": * + +#focus-slide(theme: "berry")[The Issue] +// = The issue + +== A popular language + +#align(center, image("../figs/python-logo.svg")) + +Python is a widely used language, especially in data science + +== It can bite + +Everything is fine... + +```python +transactions = load_transactions() +mean_amount = np.mean(transactions) +``` + +// Il y a comme un couac -> duck-typing + +#pause + +Until its not + +```python +def load_transactions(): + return [ + usd_transaction, + chf_transaction + ] +``` + +--- + +#{ + set text(size: 0.4em) + table( + columns: (1fr, 2fr, 0.5fr, 3.5fr), + table.header[*Height*][*Country*][*Age*][*Profession*], + // range(5 * 10).map(_ => line()) + ..range(4 * 28).map( + i => line( + start: (0%, 0%), + end: (100%, 0%), + stroke: ( + dash: ( + array: (6pt, 3pt, "dot", 3pt), + phase: calc.rem(i * 13, 31) * 1pt + ), + paint: gray + ) + ) + ) + ) + + pause + + place( + center + horizon, + dx: -2.5em, + box( + stroke: 1pt, + radius: 4pt, + width: 8em, + height: 6em, + clip: true, + fill: white, + move( + dx: -5em, + dy: -1em, + table( + columns: (6em, 6em, 6em), + rows: 1.5em, + [], [61], [], + [], [18], [], + [], [1963#place(center + horizon, ellipse(width: 2.5em, height: 2em, stroke: std.red))], [], + [], [35], [], + [], [42], [], + ) + ) + ) + ) +} \ No newline at end of file diff --git a/presentation/sections/02_state_of_the_art.typ b/presentation/sections/02_state_of_the_art.typ new file mode 100644 index 0000000..279a81e --- /dev/null +++ b/presentation/sections/02_state_of_the_art.typ @@ -0,0 +1,32 @@ +#import "../requirements.typ": * + +#focus-slide(theme: "mandarine")[What's Out There?] +// = What's out there? + +#gallery( + image("../figs/mypy_logo.svg"), + image("../figs/pyright-logo.png"), + title: [Existing Type Checkers], + columns: 2, + gutter: 3em, +) + +== Community Packages for Pandas + +=== Strictly Typing Pandas +#{ + set text(size: 0.8em) + emph(link("https://strictly-typed-pandas.readthedocs.io")) +} + +- Static typing of schema +- Quite limited + +=== Pandera +#{ + set text(size: 0.8em) + emph(link("https://pandera.readthedocs.io")) +} + +- Dynamic verification and data validation +- Powerful but verbose diff --git a/presentation/sections/03_midas.typ b/presentation/sections/03_midas.typ new file mode 100644 index 0000000..98b1ced --- /dev/null +++ b/presentation/sections/03_midas.typ @@ -0,0 +1,402 @@ +#import "../requirements.typ": * +#import "@preview/curryst:0.6.0": prooftree, rule +#import "@preview/pinit:0.2.2": pin, pinit-point-from +#import codly: codly, codly-enable, codly-disable, local +#import "@preview/cetz:0.5.2" as cetz: canvas, draw, tree +#import "@preview/cetz-plot:0.1.4": smartart + +#focus-slide(theme: "forest")[Making Python Better] +// = Making Python Better + +== Strict Typing Rules + +#let syntax(body) = { + set text(weight: "bold", fill: rgb(100, 50, 0)) + body +} + +#slide(repeat: 5, self => { + let (uncover, only, alternatives-match) = utils.methods(self) + let typing-rule = prooftree( + rule( + name: smallcaps[(T-Tern)], + uncover("3-", $Gamma tack "t"_1: "bool"$), + uncover("4-", $Gamma tack "t"_21: "T"$), + uncover("4-", $Gamma tack "t"_22: "T"$), + alternatives-match(( + "1": [], + "2-4": $"t"_21 #syntax[if] "t"_1 #syntax[else] "t"_22$, + "5-": $Gamma tack "t"_21 #syntax[if] "t"_1 #syntax[else] "t"_22: "T"$, + )) + ) + ) + + show raw: it => { + show regex("pin\d+"): it => pin(int(it.text.slice(3))) + it + } + + figure( + ```python + value = pin1e1pin2 if pin3conditionpin4 else pin5e2pin6 + ``` + ) + + pause + + /* + pinit-point-from( + (1, 2), + offset-dx: -20pt, + body-dx: -0.8em, + body-dy: -0.2em, + $"t"_21$ + ) + + pinit-point-from( + (5, 6), + offset-dx: 20pt, + body-dx: 0.2em, + body-dy: -0.2em, + $"t"_22$ + ) + + pinit-point-from( + (3, 4), + offset-dx: 0pt, + pin-dx: 0pt, + body-dx: -0.2em, + body-dy: 0.2em, + $"t"_1$ + ) + */ + + v(2cm) + + figure(typing-rule) +}) + +#codly( + highlights: ( + ( + line: 2, + start: 9, + tag: [#set text(size: 1.25em);```python int```], + fill: std.yellow + ), + ) +) +#figure( + ```python + value: float + value = 2026 + ``` +) + +#pause + +Liskov Substitution Principle $->$ Subsumption Rule + +#figure( + prooftree( + rule( + name: smallcaps[(T-Sub)], + $Gamma tack "t": "S"$, + $"S" <: "T"$, + $Gamma tack "t": "T"$, + ), + ) +) + +== Enforcing Rules + +/* +#set enum( + full: true, + numbering: (..n) => strong( + numbering( + if n.pos().len() == 1 { + "1." + } else { + "a." + }, + n.pos().last() + ) + ) +) +Steps: ++ Parsing ++ Type-checking ++ + Reporting + + Code generation +*/ + +#let palette1 = ( + std.purple, + std.blue, + std.orange, + std.green, +) +#let palette2 = ( + std.red, + std.orange, + std.green, + std.blue, +) +#let steps = ( + [Parsing], + [Type-checking], + [Reporting], + [Code generation], +) +#let palette = palette2.map(c => c.lighten(50%)) + +#{ + slide(repeat: 4, self => context { + let max-step-height = calc.max(..steps.map(s => measure(box(width: 5em, s)).height)) + + align(left + horizon, canvas({ + smartart.process.chevron( + steps.map(s => box(height: max-step-height, align(center + horizon, s))).slice(0, calc.min(steps.len(), self.subslide)), + step-style: palette, + steps: ( + max-width: 5em, + ), + ) + })) + }) +} + +#let header-process(n) = place( + top + right, + { + set text(size: 0.5em) + canvas({ + smartart.process.chevron( + steps, + step-style: palette.enumerate().map(((i, c)) => { + if i == n {c} + else {c.lighten(70%)} + }), + steps: (max-width: 5em), + equal-length: true + ) + }) + } +) + +#header-process(0) +#{ + show raw: set text(size: 0.7em) + grid( + columns: (11em, auto), + column-gutter: 1em, + row-gutter: 0.2em, + align: (x, y) => if y == 0 {center} else if x == 0 and y == 2 {top + right} else {left}, + grid.header[*Source*][*Raw AST*], + ```python + value = 3.0 + int("4") + ```, + grid.cell(rowspan: 2, uncover("2-", ```python + Module( + body=[ + Assign( + targets=[ + Name(id='value')], + value=BinOp( + left=Constant(value=3.0), + op=Add(), + right=Call( + func=Name(id='int'), + args=[ + Constant(value='4')], + keywords=[])))]) + ```)), + uncover("2-", canvas({ + draw.bezier-through( + (0, 0), + (1.5, -2.5), + (5, -4), + mark: (end: ">", fill: black), + name: "arrow" + ) + draw.content( + (1.5, -2.5), + ```python ast.parse(source)```, + anchor: "north-east", + padding: 3pt, + ) + })) + ) +} + +--- + + +#header-process(0) +#{ + show raw: set text(size: 0.7em) + grid( + columns: (auto, auto), + column-gutter: 1em, + row-gutter: 0.2em, + align: (x, y) => if y == 0 {center} else if x == 0 and y == 2 {top + right} else {left}, + grid.header[*Raw AST*][*Custom AST*], + ```python + Module( + body=[ + Assign( + targets=[ + Name(id='value')], + value=BinOp( + left=Constant(value=3.0), + op=Add(), + right=Call( + func=Name(id='int'), + args=[ + Constant(value='4')], + keywords=[])))]) + ```, + local( + inset: (x: 0.32em, y: 0.15em), + lang-outset: (x: 0.32em, y: 0.2em), + ```AST + AssignStmt + ├── targets + │ └── [0] VariableExpr + │ └── name: value + └── value + └── BinaryExpr + ├── left + │ └── LiteralExpr + │ └── value: 3.0 + ├── operator: Add + └── right + └── CallExpr + ├── callee + │ └── VariableExpr + │ └── name: int + ├── arguments + │ └── [0] LiteralExpr + │ └── value: '4' + └── keywords + ``` + ) + ) +} + +--- + +#header-process(1) + +#let cetz-canvas = touying-reduce.with(cetz) +#let typing-tree = cetz-canvas({ + draw.set-style(content: (padding: 0.5em)) + tree.tree( + name: "tree", + ([`type_of`(```py 3.0 + int("4")```)], + [`type_of`(```py 3.0```)], + ([`type_of`(```py int("4")```)], + [`type_of`(```py int```)], + [`type_of`(```py "4"```)], + ) + ) + ) + let type(pos, typ) = { + draw.content( + "tree.g" + pos.map(str).join("-") + ".south", + box( + inset: (x: 0.4em, y: 0.2em), + stroke: std.orange, + fill: std.orange.lighten(90%), + radius: 0.4em, + { + set text(size: 0.8em) + raw(lang: "python", typ) + } + ), + padding: 0pt, + anchor: "north" + ) + } + + (pause,) + type((0, 0), "float") + type((0, 1, 1), "str") + (pause,) + type((0, 1, 0), "(Any, /) -> int") + (pause,) + type((0, 1), "int") + (pause,) + draw.content( + ( + rel: (3, 0), + to: ( + "tree.g0.south", + 50%, + "tree.g0-1.north", + ) + ), + { + set text(size: 0.8em) + ```py float.__add__(int) -> float``` + }, + anchor: "west" + ) + (pause,) + type((0,), "float") +}) + +#align(center + horizon, typing-tree) + +--- + +#import "@preview/conch:0.1.0" + +#let midas-outputs = ( + check: read("../figs/midas_output.txt"), + types: read("../figs/midas_output2.txt"), +) + +#let midas-cmd(args, stdin, files) = { + ( + stdout: midas-outputs.at(args.first()), + exit-code: 0 + ) +} + +#let frames = conch.terminal-frames( + system: conch.system( + plugins: ( + ("midas", midas-cmd), + ), + files: ( + "script.py": read("../figs/script.py") + ), + hostname: "XANA", + ), + user: "louis", + commands: ( + "cat script.py", + "midas check script.py", + "midas types script.py", + "", + ), + mode: "key-frames", + show-cursor: false, + theme: "monokai", + height: 300pt +) + +#slide(repeat: frames.len() - 2, self => [ + #header-process(2) + #figure( + align(left + top, frames.at(self.subslide)) + ) +]) + +== Runtime Verification + +== Customization + +== Advanced Typing \ No newline at end of file diff --git a/presentation/sections/04_status_and_future.typ b/presentation/sections/04_status_and_future.typ new file mode 100644 index 0000000..0cd9a0a --- /dev/null +++ b/presentation/sections/04_status_and_future.typ @@ -0,0 +1,4 @@ +#import "../requirements.typ": * + +#focus-slide(theme: "neon")[Current Status and Future] +// = Current Status and Future \ No newline at end of file