# Aerial - towards "production ready" This document lists / sumarizes Aerial's "areas of interest" for Pawel: semantics and public API surface that IMO needs discussion before embarking on the "production ready" implementation. ## Signal creation Current API proposal: ```typescript! const name = signal('Pawel'); ``` Discussion: * **"bike-shading" name**: ~~"state" might be interpreted something "bigger" than just a signal. How about `createSignal` or just `signal` ?~~ => renamed to `signal` for now, but we might revisit the name since there are 2 concepts being discussed here: * a concept / primitive of "value changing over time" + "changed / dirty notification" - this concept in itself doesn't indicate how the value is actually changed (mutable API); * a signal (above concept) + mutation functionality * **notation of equality**: many signal libraries have the additional option of providing the "signal equality" function (usually defaulting to `Object.is`). This is a tricky therithory for aerial: * our computed signals don't care about the equality (will propagete diry flag regardless of the actual value); * we want to work with mutable data structures so our default equality would need to compare primitive values only; * **Pawel's take**: skip the notation of equality for now. ## Reading signal's value ```typescript! console.log(signal()); // it is just a getter! ``` Discussion: * we might want to consider overriding `toString()` implementation as the default one from function will print stringified function body. ## Signal types / branding Current API proposal: ```typescript! export type Signal<T> = () => T; const SIGNAL = Symbol('SIGNAL'); export function isSignal<T>(value: unknown): value is () => T { return (value as any)[SIGNAL] === true; } ``` Discussion: * do we need separate types for read-only and read-write signals? * do we need "runtime branding" of signals in the public API surface? ## Signal change Current API proposal: ```typescript! signal.set('new value') ``` Discussion: * do we need a specific type to denote a read / write signal? ## Signal update Current API: ```typescript! class MyCmp { counter = createState(0); increment() { this.counter.set(this.counter() + 1); } } ``` Discussion: * `this.counter.set(this.counter() + 1)` becomes verbose, options: * `this.counter.set(c => c + 1)` - passing a function is interpreted as "update function" - this might pose a problem where signal is supposed to hold the actual function as a value; * `this.counter.update(c => c + 1)` - pretty clear, but larger API surface; ## Non-reactive reads Most of the signal libraries have APIs to read signals value _without_ creating connection in the reactive graph (subscribing to future changes). Current API proposal: ```typescript! const baz = computed(foo() + peek(bar())) ``` In the above example the computation will be re-executed whenever `foo()` changes, but updating `bar()` value will _not_ trigger re-computation. Discussion: * ~~`nonReactiveRead` is probably too long as the name. How about: `nonReactive` or `untracked`? => `peek` is the current proposal~~ ## Reads outside of the reactive context Current Aerial implementation will throw whenever someone tries to _read_ a signal outside of a "reactive context" (`computed`, `effect` etc.). This requires a "reactive context" wrapper and: * it is not clear to me how this would be represented in an Angualr application; * might limit signals usage (read) in the 3rd party code and / or force depenency on Aerial (`nonReactiveRead`) Check behaviour of other libraries: * SolidJS - allows reads anywhere * Preact - ? * Vue - ? ## Effects Current API proposal: ```typescript! const effectRef = effect(() => { console.log('changed: ', signal()); }); ``` where `effectRef` has 2 methods: * `schedule` - schedule effect execution (exact evaluation time depending on the scheduler abstraction); * `destroy` - cleanup effect (so it stops executting, regardless of the dependencies change) Effects will require bigger discussion around: * types of effects and their execution timing in the application lifecycle; * having `schedule` API (should people be allowed to "manually" schedule); * cleanup * async effects * ... ### API Proposal - Alex ```typescript=\ const disabled: SettableSignal = signal(true); interface SettableSignal<T> extends Signal<T> { // replace the value with a different one (and mark dirty) set(newValue: T): void; // maybe update the value with a function (and mark dirty) // if the function is not passed, this just marks the signal as dirty // (presumably the value was mutated out of band) update(fn?: (value: T) => T): void; } const todoStore: Store<Todo[], ??> = createStore<Todo[]>(...); // Stores are also signals, but have some custom API that's defined somehow. type Store<Todo[], ??> = Signal<Todo[]> & ...; // no need to distinguish the type here - just a reactive value with no extra API const derived: Signal<boolean> = computed(value => !value); // same here const rxjsSignal: Signal<Thing> = useObservable(obs$); // same here - signal-based inputs are just reactive values @Input({signal: true}) myInput!: Signal<string>; ```