Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 38 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -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!
73 changes: 37 additions & 36 deletions packages/functorial/README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -47,46 +42,52 @@ 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](<assets/mental_model.png>)

### ... 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`
> already knows what happens, so it would be a pure waste to discard this
> 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.

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

Expand All @@ -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,
Expand Down Expand Up @@ -136,7 +137,7 @@ The library can be loaded directly from `esm.sh`
</script>
```

### Installation
### Package managers

Depending on your package manager:

Expand All @@ -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
Expand Down
12 changes: 12 additions & 0 deletions packages/functorial/SIGNALS.md
Original file line number Diff line number Diff line change
@@ -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](<assets/signals_mental_model.png>)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.