rip-lang 3.7.4 → 3.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,311 +0,0 @@
1
- <img src="https://raw.githubusercontent.com/shreeve/rip-lang/main/docs/rip.png" style="width:50px" /> <br>
2
-
3
- # Rip Reactivity
4
-
5
- Rip implements a **fine-grained reactive system** that rivals and often exceeds the capabilities of major frameworks like Vue, Svelte, Solid, and React — in just ~200 lines of runtime code.
6
-
7
- ## The Reactive Triad
8
-
9
- Rip's entire reactivity model is built on three foundational concepts:
10
-
11
- | Primitive | Description | Read As | Role | Purpose |
12
- |-----------|-------------|---------|------|---------|
13
- | `:=` | state | "has state" | **Source** | Where reactive data originates |
14
- | `~=` | computed | "always equals" | **Derivation** | Computed values (lazy, cached) |
15
- | `~>` | effect | "reacts to" | **Reaction** | Side effects when data changes |
16
-
17
- These three primitives are the **minimal complete set** for reactive programming — everything React, Vue, Svelte, and Solid can do with state management, Rip can do too.
18
-
19
- ---
20
-
21
- ## Why These Three Are Complete
22
-
23
- Every reactive system reduces to these three concepts:
24
-
25
- | Framework | Source | Derivation | Reaction |
26
- |-----------|--------|------------|----------|
27
- | **Rip** | `:=` | `~=` | `~>` |
28
- | Angular | `signal()` | `computed()` | `effect()` |
29
- | Imba | `@property` | implicit | implicit |
30
- | MobX | `observable` | `computed` | `autorun` |
31
- | Next.js | `useState` | `useMemo` | `useEffect` |
32
- | React | `useState` | `useMemo` | `useEffect` |
33
- | Solid | `createSignal` | `createMemo` | `createEffect` |
34
- | Svelte 4 | `let x` | `$: x` | `$: {}` |
35
- | Svelte 5 | `$state` | `$derived` | `$effect` |
36
- | Vue | `ref()` | `computed()` | `watch()` |
37
-
38
- ### What You Can Build From These Three
39
-
40
- - **Stores** → objects with state properties
41
- - **Two-way binding** → state + effect that syncs
42
- - **Async resources** → state + effect that fetches
43
- - **Event handling** → update state → triggers reactions
44
- - **Derived stores** → computed from other state
45
-
46
- ### What's Missing Without Any One
47
-
48
- - **Without `:=`** → No source of truth
49
- - **Without `~=`** → Manual tracking or inefficient effects
50
- - **Without `~>`** → Can't react to changes (no side effects)
51
-
52
- ---
53
-
54
- ## Quick Example
55
-
56
- ```coffee
57
- count := 0 # count has state 0
58
- doubled ~= count * 2 # doubled always equals count * 2
59
- logger ~> console.log count # reacts to count changes
60
- ~> console.log count # same thing, but the "fire and forget" version
61
-
62
- increment: -> count += 1
63
-
64
- increment() # Logs: 1
65
- increment() # Logs: 2
66
- ```
67
-
68
- ---
69
-
70
- ## How It Works
71
-
72
- ### State (`:=`) — "has state"
73
-
74
- State creates a **reactive container** that tracks its readers and notifies them on change.
75
-
76
- ```coffee
77
- count := 0 # count has state 0
78
- count += 1 # Update triggers dependents
79
- ```
80
-
81
- **Compiles to:**
82
- ```javascript
83
- const count = __state(0);
84
- count.value += 1;
85
- ```
86
-
87
- **What happens internally:**
88
- 1. Reading `count.value` inside an effect/computed **tracks** the dependency
89
- 2. Writing to `count.value` **notifies** all subscribers
90
- 3. Effects re-run, computeds mark dirty
91
-
92
- ### Computed (`~=`) — "always equals"
93
-
94
- Computed creates a **computed value** that automatically updates when dependencies change.
95
-
96
- ```coffee
97
- count := 0
98
- doubled ~= count * 2 # doubled always equals count * 2
99
- ```
100
-
101
- **Key features:**
102
- - **Lazy** — only computes when read
103
- - **Cached** — won't recompute unless dependencies change
104
- - **Chainable** — computeds can depend on other computeds
105
-
106
- ### Effect (`~>`) — "reacts to"
107
-
108
- The effect operator runs **side effects** when dependencies change. Dependencies are auto-tracked from reactive values read in the body.
109
-
110
- ```coffee
111
- ~> document.title = "Count: #{count}"
112
- ```
113
-
114
- **Key features:**
115
- - **Auto-tracking** — dependencies detected automatically from body
116
- - **Immediate** — runs once immediately to establish dependencies
117
- - **Controllable** — optionally assign to a variable to control the effect
118
-
119
- **Syntax:**
120
- ```coffee
121
- # Fire and forget (no assignment)
122
- ~> console.log count
123
-
124
- # Controllable (assign to variable)
125
- logger ~> console.log count
126
- logger.stop! # Pause reactions
127
- logger.run! # Resume reactions
128
- logger.cancel! # Permanent disposal
129
- ```
130
-
131
- ---
132
-
133
- ## Comparison with Major Frameworks
134
-
135
- ### State
136
-
137
- | Feature | Rip | Vue | Solid | React |
138
- |---------|:---:|:---:|:-----:|:-----:|
139
- | Auto-tracking on read | ✅ | ✅ | ✅ | ❌ |
140
- | Same-value skip | ✅ | ✅ | ✅ | ✅ |
141
- | Re-entry protection | ✅ | ❌ | ❌ | ❌ |
142
- | Lock for SSR | ✅ | ❌ | ❌ | ❌ |
143
- | Cleanup/disposal | ✅ | ✅ | ✅ | ❌ |
144
- | Raw read (untracked) | ✅ | ✅ | ✅ | ❌ |
145
- | Primitive coercion | ✅ | ❌ | ❌ | N/A |
146
-
147
- **Rip advantage:** `.lock()`, `.kill()`, `.read()` utilities that others lack.
148
-
149
- ### Computed
150
-
151
- | Feature | Rip | Vue | Solid | MobX |
152
- |---------|:---:|:---:|:-----:|:----:|
153
- | Lazy evaluation | ✅ | ✅ | ✅ | ✅ |
154
- | Cached until deps change | ✅ | ✅ | ✅ | ✅ |
155
- | Dirty propagation | ✅ | ✅ | ✅ | ✅ |
156
- | Auto dependency cleanup | ✅ | ✅ | ✅ | ✅ |
157
- | Chainable | ✅ | ✅ | ✅ | ✅ |
158
- | Read without tracking | ✅ | ❌ | ❌ | ❌ |
159
- | Lock (freeze value) | ✅ | ❌ | ❌ | ❌ |
160
-
161
- **Rip advantage:** `.read()` for untracked access, `.lock()` to freeze.
162
-
163
- ### Effect (`~>`)
164
-
165
- | Feature | Rip | Vue | Svelte | React |
166
- |---------|:---:|:---:|:------:|:-----:|
167
- | Auto-tracking | ✅ | ✅ | ✅ | ❌ |
168
- | No manual deps array | ✅ | ✅ | ✅ | ❌ |
169
- | Runs immediately | ✅ | ✅ | ✅ | ✅ |
170
- | Controllable (stop/run) | ✅ | ✅ | ✅ | ❌ |
171
- | Re-runs on change | ✅ | ✅ | ✅ | ✅ |
172
-
173
- **Rip advantage over React:** No manual dependency arrays!
174
-
175
- ```javascript
176
- // React - manual, error-prone
177
- useEffect(() => {
178
- document.title = `Count: ${count}`;
179
- }, [count]); // 😩 Must list deps manually
180
-
181
- // Rip - automatic
182
- ~> document.title = "Count: #{count}" // 🎉 Deps tracked automatically
183
- ```
184
-
185
- ### Bundle Size
186
-
187
- | Framework | Runtime Size |
188
- |-----------|-------------|
189
- | React | ~40 KB (minified) |
190
- | Vue | ~34 KB |
191
- | Svelte | ~2 KB (but grows with app size) |
192
- | **Rip** | **~4 KB** (full runtime) |
193
-
194
- ---
195
-
196
- ## Advanced Features
197
-
198
- ### Batching
199
-
200
- Group multiple updates into a single flush:
201
-
202
- ```javascript
203
- __batch(() => {
204
- count.value = 1;
205
- name.value = "Alice";
206
- // Effects run once at the end, not twice
207
- });
208
- ```
209
-
210
- ### Untracked Reads
211
-
212
- Read a value without creating a dependency:
213
-
214
- ```javascript
215
- const currentValue = count.read(); // No tracking
216
- ```
217
-
218
- ### Locking (SSR/Hydration)
219
-
220
- Prevent writes during server-side rendering or freeze values:
221
-
222
- ```javascript
223
- count.lock(); // Now immutable
224
- ```
225
-
226
- ### Cleanup
227
-
228
- Dispose of reactive values:
229
-
230
- ```javascript
231
- const finalValue = count.kill(); // Returns value, clears subscribers
232
- ```
233
-
234
- ---
235
-
236
- ## The Architecture
237
-
238
- ```
239
- ┌─────────────┐ reads ┌─────────────┐
240
- │ STATE │◄──────────────│ EFFECT │
241
- │ count │ │ (side fx) │
242
- └──────┬──────┘ └─────────────┘
243
- │ ▲
244
- │ notifies │ triggers
245
- ▼ │
246
- ┌─────────────┐ reads ┌──────┴──────┐
247
- │ COMPUTED │◄──────────────│ EFFECT │
248
- │ doubled │ │ (logger) │
249
- └─────────────┘ └─────────────┘
250
- ```
251
-
252
- 1. **State** is the source of truth
253
- 2. **Computed** derives from state (lazy, cached)
254
- 3. **Effects** react to state/computed changes
255
- 4. **Changes propagate** through the dependency graph
256
-
257
- ---
258
-
259
- ## Why Fine-Grained Reactivity?
260
-
261
- Rip uses **fine-grained reactivity** (like Vue/Solid), not Virtual DOM diffing (like React).
262
-
263
- | Approach | How it works | Pros | Cons |
264
- |----------|--------------|------|------|
265
- | **VDOM** (React) | Re-render tree, diff, patch | Simple mental model | Overhead, requires optimization |
266
- | **Fine-grained** (Rip) | Track dependencies, update directly | Surgical updates, fast | More complex internally |
267
-
268
- ### Result
269
-
270
- - **No VDOM overhead** — changes propagate directly to subscribers
271
- - **Surgical updates** — only the exact things that changed update
272
- - **No `useMemo`/`useCallback` dance** — caching is automatic
273
- - **Smaller bundles** — no diffing algorithm needed
274
-
275
- ---
276
-
277
- ## Summary
278
-
279
- Rip's reactivity system:
280
-
281
- ✅ **The Reactive Triad** — state (`:=`), computed (`~=`), effect (`~>`) <br>
282
- ✅ **Natural reading** — "has state", "always equals", "reacts to" <br>
283
- ✅ **Minimal complete set** — same model as Vue, Solid, MobX <br>
284
- ✅ **Lazy computed** — only calculates when needed <br>
285
- ✅ **Fine-grained** — surgical updates to subscribers <br>
286
- ✅ **Controllable effects** — `.stop!`, `.run!`, `.cancel!` when needed <br>
287
- ✅ **Tiny runtime** — ~200 lines, ~4 KB <br>
288
- ✅ **Extra utilities** — `.lock()`, `.read()`, `.kill()` that others lack <br>
289
-
290
- **On par with Vue/Solid. Better than React. A fraction of the size.**
291
-
292
- ---
293
-
294
- ## Types and Reactivity
295
-
296
- Reactive operators work with Rip's optional type system:
297
-
298
- ```coffee
299
- count:: number := 0 # Typed state
300
- doubled:: number ~= count * 2 # Typed computed
301
- ```
302
-
303
- Type annotations are erased from `.js` output. In `.d.ts` output, reactive
304
- state emits `Signal<T>` and computed values emit `Computed<T>`:
305
-
306
- ```ts
307
- declare const count: Signal<number>;
308
- declare const doubled: Computed<number>;
309
- ```
310
-
311
- See [RIP-TYPES.md](RIP-TYPES.md) for the complete type system specification.