diff --git a/README.md b/README.md index 067d2cb..e579fde 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,52 @@ # F-Stack -_Rethinking fullstack development_ - -Here is my experimental stack. If you like when things fall neatly into place in -a clean, coherent and holistic design then this is for you too. - -## Values - -1. The focus is on having **clean mental models** for every part of the stack. - Which gives you a superpower: **understanding**. When you can reason clearly - about your own application code then you're in a good, future proof, place. - No magic. -2. I'm not after performance for it's own sake. It's JavaScript man, not Rust. - As long as performance is good, I prefer clean, readable, maintainable code. - I don't want to need 10 coffees to understand what I wrote. -3. I'm not chasing DX above all. Oftentimes too much comfort means "magic" - solutions, where we loose track of what's going on. This quickly becomes - un-debuggable and impossible to reason about. I don't trade long term - understanding for immediate comfort. -4. The stack is **unapologetic** with concepts: I'll use the correct - terminology, without hiding from math or computer science terms, and without - rebranding them. I believe the added value of manual programming is learning, - [theory building](https://pages.cs.wisc.edu/~remzi/Naur.pdf) and **acquiring - expertise**. In other words, the role of a framework is not only to provide - you with a set of tools, but with a deeper knowledge and vision of the field. +Welcome to this repo where we explore new wild ideas _rethinking fullstack web +development_ + +## Why? + +We love the web. But the consensus is growing that the tech we use has gone too +far, with too many abstractions and layers, that make it impossible for a single +person to have a comprehensive vision and understanding of his own stack. Even +the application code we write can easily become hard to debug and reason about +in this context. + +In parallel, the Platform is maturing more and more, and things that were +previously impossible without compilers, bundlers, transpilers and complex +build-chains can now be done natively, sometimes even without JS. This evolution +empowers us developers to simplify our stack drastically... + +## Principles + +The guiding principles of this new stack are the following: + +1. **Standards First** We use platform APIs: this means less library code, less + maintenance, and more experience with APIs that are here to stay. +2. **Type Safety** We want type-safe applications, and we develop the stack with + types in mind. +3. **Minimalism and Clarity** Fewer abstractions, and just the right ones. The + focus is on having **clear mental models** allowing a single person to + understand the whole stack with a coherent vision. No magic. ## Architecture The current pieces are (more to come): -### [Functorial](./packages/functorial/README.md) +### [Type-strip](https://github.com/fcrozatier/type-strip) + +A simple and fast type stripper. -A new `Proxy`-based reactivity system, that goes beyond Signals. More idiomatic. -More granular. +### [Functorial](./packages/functorial/README.md) -It's a structured way to declaratively interact with web APIs in a reactive -manner +A new `Proxy`-based reactivity system, that goes beyond Signals. It's more +idiomatic, more granular, more declarative and doesn't require diffing. ### [Reflow](./packages/reflow/README.md) A minimal frontend framework powered by template tags and providing Functorial mappings to all common web APIs. + +## Can I contribute? + +Sure! Feel free to experiment with the code, open issues, start the +conversation. We won't bite, promise! diff --git a/packages/functorial/README.md b/packages/functorial/README.md index a132519..4a10b75 100644 --- a/packages/functorial/README.md +++ b/packages/functorial/README.md @@ -1,43 +1,38 @@ # Functorial -_The reactivity primitive_ +A new `Proxy`-based reactivity system, that goes beyond Signals. ## Introduction -Functorial reactivity is an idiomatic way to interact with web APIs in a -faithful, reactive and declarative manner. It's different from Signals as you -not only map the data, but also the behaviors, in a structured way. +Functorial is a reactivity system where you not only map the data, but also the +behaviors, in a structured, granular, reactive and declarative way. For example, `delete` an object property to remove a listener or call `unshift()` on a list to prepend data in a DOM container. As a consequence, this approach yields -- The highest level of granularity: faithfulness -- A cristal-clear [mental model](#mental-model) -- A principled approach to interacting with web APIs declaratively -- A more natural reactivity primitive +- The highest level of granularity: [**faithfulness**](#faithfulness) +- A cristal-clear [**mental model**](#mental-model) +- A principled approach to interacting with web APIs **declaratively** +- A more **natural** reactivity primitive ## Mental Model -### Mutation-first +### Mutations-first -The DOM is a mutable structure, and for performance we update the DOM by +The DOM is a mutable structure, and for performance reasons we update the DOM by mutating it. Functorial reactivity reflects this in our templates with its focus -on mutable structures. State is held inside mutable structures (_eg._ `Object`, -`Array`) and their changes and operations are transported to corresponding DOM -updates. +on mutable structures. State is held inside mutable structures and their changes +and operations are reflected to corresponding DOM updates. For example add a key-value pair to an object to add an attribute to a DOM -`Element`, delete it to remove the attribue. +`Element`, `delete` it to remove the attribue. -### Mapping Templates to DOM... +### A natural mapping from Templates to DOM Templates are where we declare the relation between a piece of state and the -DOM. - -With Functorial reactivity this relation is a faithful communication between our -templates and the DOM: +DOM. With Functorial reactivity this relation is a faithful communication: 1. We create a piece of state and map it to the DOM (up arrow) 2. We mutate the state or perform an operation on it (left arrow) @@ -47,21 +42,28 @@ templates and the DOM: 4. The DOM is in the same state as if we had directly applied this updated state: the whole diagram commutes (down arrow) +> [!NOTE] +> Notice the `f(op)` on the right arrow. It's a subtle difference with the +> [mental model for Signals](/packages/functorial/SIGNALS.md). In functorial +> reactivity the operation (_eg_ `unshift`) is provided by the `Proxy` to our +> listeners callback which can then reflect it (_eg_ `prepend`), while with +> signals we have no clue and must resort to diffing on every change. + ![Mental Model]() -### ... faithfully +### Faithfulness -The relation between our templates and the DOM being faithful means that we +The relation between our templates and the DOM being **faithful** means that we both: - **know the full story of what happens on the Template side** - **can reach whole APIs dynamically on the DOM side** -Since it's `Proxy`-based, the granularity on the left side is only constrained -by the resolution provided by the `Proxy` traps: we can know wether a key was -created, updated, deleted, or whether a method was called and with which -arguments. This tells us the full story as an event, which can be accessed as -the callback parameter of the `listen` function. +Since it's `Proxy`-based, the granularity on the left side (Template side) is +only constrained by the `Proxy` traps: we can know wether a key was created, +updated, deleted, or whether a method was called and with which arguments. This +tells us the full story as an event, which can be accessed as the callback +parameter of the `listen` function. > [!NOTE] > In particular this granularity means we don't need diffing: the `Proxy` @@ -69,7 +71,7 @@ the callback parameter of the `listen` function. > information to then reconstruct it afterwards with diffing. Instead this data > is provided as a `ReactiveEvent` in the `listen` callback parameter. -### Expressive, Structured approach +### Expressive, Declarative, Structured approach Functorial reactivity focuses on mutable **structures**: the `listen` function takes a structure to listen to as its first parameter. @@ -77,16 +79,15 @@ takes a structure to listen to as its first parameter. This, combined with the granularity of the `ReactiveEvent` which tells us how the structure changes, let us create expressive mappings of semantics. -For example, since all common web APIs all revolve around create and delete -operations, +For example, since all common web APIs all revolve around multiple operations - `setAttribute` and `removeAttribute` - `addEventListener` and `removeEventListener` - `.classList.add` and `.classList.remove` - `.style.setProperty` and `.style.removeProperty` -the `delete` operation on a state object can be mapped to the expected -corresponding operation in the DOM. Idiomatic and expressive. +the `delete` operation on a state object can be directly mapped to the expected +corresponding operation in the DOM. Idiomatic and expressive, with no diffing. ### Videos @@ -97,7 +98,7 @@ few examples: - [Mental model and difference with Signals](https://bsky.app/profile/fred-crozatier.dev/post/3m3ctprjykc25) - [Example of complex operations on a list](https://bsky.app/profile/fred-crozatier.dev/post/3m3cvi5ygec25) -## Usage +## Installation Functorial is a low-level, framework-independent reactivity system. You can use it directly but will have to implement common web mappings (attributes, @@ -136,7 +137,7 @@ The library can be loaded directly from `esm.sh` ``` -### Installation +### Package managers Depending on your package manager: @@ -146,10 +147,10 @@ pnpm i jsr:@f-stack/functorial npx jsr add @f-stack/functorial ``` -## Examples +## Usage Examples -Here are a few examples showcasing some basic features. You can also have a look -at the [Playground](../../playground/README.md) for real life examples and usage +Here are a few examples showcasing basic features. You can also have a look at +the [Playground](../../playground/README.md) for real life examples and usage with Reflow. ### Reactive objects diff --git a/packages/functorial/SIGNALS.md b/packages/functorial/SIGNALS.md new file mode 100644 index 0000000..a604846 --- /dev/null +++ b/packages/functorial/SIGNALS.md @@ -0,0 +1,12 @@ +# Signals mental model + +1. We create a piece of state and map it to the DOM in a setup function which + runs inside an effect (up arrow) +2. We mutate the state or perform an operation on it (left arrow) +3. The setup function from point 1 runs again, with no clue as to what change + happened. (down arrow) +4. We need to update the DOM but recreating it wouldn't be optimal so we need to + resort to diffing, keying or other tricks to make a performant update and + compensate for the lack of info about what happened (right arrow) + +![Signals Mental Model]() diff --git a/packages/functorial/assets/signals_mental_model.png b/packages/functorial/assets/signals_mental_model.png new file mode 100644 index 0000000..b779ed4 Binary files /dev/null and b/packages/functorial/assets/signals_mental_model.png differ