@@ -20,7 +20,64 @@ And yet, building with the best-in-class version of every tool still produces ~5
2020
2121<!-- more -->
2222
23- Let's walk through the app section by section - Jac first to show the intent, then the SOTA equivalent to show the cost.
23+ ## The Complete Jac App
24+
25+ Here's the Jac version. One file, 46 lines:
26+
27+ ``` jac
28+ node Todo {
29+ has title: str,
30+ category: str = "other",
31+ done: bool = False;
32+ }
33+
34+ enum Category { WORK, PERSONAL, SHOPPING, HEALTH, OTHER }
35+
36+ def categorize(title: str) -> Category by llm();
37+
38+ def:pub add_todo(title: str) -> Todo {
39+ try {
40+ category = str(categorize(title)).split(".")[-1].lower();
41+ } except Exception { }
42+ root() ++> (todo := Todo(title=title, category=category));
43+ return todo;
44+ }
45+
46+ def:pub get_todos -> list[Todo] {
47+ return [root()-->][?:Todo];
48+ }
49+
50+ cl def:pub app -> JsxElement {
51+ has todos: list[Todo] = [],
52+ text: str = "";
53+
54+ async can with entry {
55+ todos = await get_todos();
56+ }
57+
58+ async def add {
59+ if text.strip() {
60+ todo = await add_todo(text.strip());
61+ todos = todos + [todo];
62+ text = "";
63+ }
64+ }
65+
66+ return
67+ <div>
68+ <input
69+ value={text}
70+ onChange={lambda e: ChangeEvent { text = e.target.value;}}
71+ onKeyPress={lambda e: KeyboardEvent { if e.key == "Enter" { add(); }}}
72+ placeholder="Add a todo..."
73+ />
74+ <button onClick={add}>Add</button>
75+ {[<p key={jid(t)}>{t.title} ({t.category})</p> for t in todos]}
76+ </div>;
77+ }
78+ ```
79+
80+ Now let's walk through each section and see what the same intent costs in the SOTA stack.
2481
2582---
2683
@@ -620,67 +677,6 @@ The 187 extra lines aren't wrong. They're just not doing what you hired them to
620677
621678---
622679
623- ## The Complete Jac App
624-
625- You've now seen each piece in context. Here's the whole thing - one file, 46 lines:
626-
627- ``` jac
628- node Todo {
629- has title: str,
630- category: str = "other",
631- done: bool = False;
632- }
633-
634- enum Category { WORK, PERSONAL, SHOPPING, HEALTH, OTHER }
635-
636- def categorize(title: str) -> Category by llm();
637-
638- def:pub add_todo(title: str) -> Todo {
639- try {
640- category = str(categorize(title)).split(".")[-1].lower();
641- } except Exception { }
642- root() ++> (todo := Todo(title=title, category=category));
643- return todo;
644- }
645-
646- def:pub get_todos -> list[Todo] {
647- return [root()-->][?:Todo];
648- }
649-
650- cl def:pub app -> JsxElement {
651- has todos: list[Todo] = [],
652- text: str = "";
653-
654- async can with entry {
655- todos = await get_todos();
656- }
657-
658- async def add {
659- if text.strip() {
660- todo = await add_todo(text.strip());
661- todos = todos + [todo];
662- text = "";
663- }
664- }
665-
666- return
667- <div>
668- <input
669- value={text}
670- onChange={lambda e: ChangeEvent { text = e.target.value;}}
671- onKeyPress={lambda e: KeyboardEvent { if e.key == "Enter" { add(); }}}
672- placeholder="Add a todo..."
673- />
674- <button onClick={add}>Add</button>
675- {[<p key={jid(t)}>{t.title} ({t.category})</p> for t in todos]}
676- </div>;
677- }
678- ```
679-
680- Data model, AI categorization, API, and full-stack reactive UI. No ORM, no prompt template, no fetch ceremony, no type mirroring. One type system from database to browser to LLM.
681-
682- ---
683-
684680## Bonus: The Configuration Tax
685681
686682The scorecard above covers application code only. But the polyglot tax extends to project configuration too - and the ratio there tells its own story.
0 commit comments