Skip to content

Commit

Permalink
add placeholder partial and interactive dev pages
Browse files Browse the repository at this point in the history
  • Loading branch information
Mario T. Lanza committed May 27, 2024
1 parent 99fdb34 commit 3c034b5
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 30 deletions.
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Write [ClojureScript](https://clojurescript.org) in JavaScript without a transpi
Highlights:

* well suited for web apps
* deploy the code you write — point free, composed, pipelines, no build required
* deploy the code you write — [point-free pipelines and partial application](/placeholder-partial.md), no build required
* implements much Clojure's standard library
* [functional core](src/core), [imperative shell](src/shell) w/ FRP
* [nil-punning](https://ericnormand.me/article/nil-punning) handles null in sensible ways
Expand All @@ -17,8 +17,6 @@ Atomic is [functional first](functional-first.md). Functions are preferred to m

Atomic has no [maps](https://clojuredocs.org/clojure.core/hash-map) or [vectors](https://clojuredocs.org/clojure.core/vector) but used objects and arrays as faux [records and tuples](https://tc39.es/proposal-record-tuple/) since before these new types were even proposed. It had previously integrated them via [Immutable.js](https://immutable-js.com) but, in practice, using objects and arrays as persistents worked well enough it wasn't worth the cost of another library. Its integration [was dropped](https://github.com/mlanza/atomic/commit/8e1787f6974df5bfbb53a371a261e09b5efee8ee). This bit of history serves to demonstrate how protocols can seamlessly integrate disparate types into a consistent api. Concrete types, even third-party ones, can be regarded as abstract things, by their apis.

Don't care for [point programming](tests/autopartial-less.js)? [Autopartial](tests/autopartial.js) delivers [point-free programming](https://en.wikipedia.org/wiki/Tacit_programming) without a build step up until [pipeline operators](https://github.com/tc39/proposal-pipeline-operator) and [partial application](https://github.com/tc39/proposal-partial-application) reach stage maturity.

## Premise
Atomic was born from an experiment answering:

Expand Down Expand Up @@ -53,7 +51,7 @@ npm install
npm run bundle
```

Copy the contents of `dist` to `lib` in a project then import from either `lib\atomic` or `lib\atomic_` depending on whether [autopartial](./tests/autopartial.js) is wanted.
Copy the contents of `dist` to `libs` in a project then import from either `libs\atomic` or `libs\atomic_` depending on whether [placeholder partial](./placeholder-partial.md) is wanted.

Implementing a small app is a good first step for someone unfamiliar with the herein described approach.

Expand All @@ -63,7 +61,7 @@ The `core` module is the basis for the functional core, `shell` for the imperati

Its state container, the bucket which houses an app's big bang [world state](https://docs.racket-lang.org/teachpack/2htdpuniverse.html), is the `cell`. It's mostly equivalent to a Clojure atom. The main exception is it invokes the callback upon subscription the way an Rx [subject](https://rxjs.dev/guide/subject) does. This is well suited to the developing of user interfaces. And like [xstream](https://staltz.com/why-we-built-xstream.html) it doesn't rely on many operators, providing a simple but sufficient platform for FRP.

The typical UI imports the trifecta—`core`, `shell`, and `dom`—as `_`, `$` and `dom` respectively. The `_` doubles as a partial application placeholder when using autopartial. To facilitate interactive development these assignments can be readily imported by entering [`cmd()`](./dist/cmd.js) from a browser console where Atomic is loaded.
The typical UI imports the trifecta—`core`, `shell`, and `dom`—as `_`, `$` and `dom` respectively. The `_` doubles as a partial application placeholder when using [placeholder partial](./placeholder-partial.md). [To facilitate interactive development](./interactive-development.md) these can be readily imported into the console.

Since many of its core functions are taken directly from Clojure one can often use its documentation. Here are a handful of its bread and butter functions:
* [`swap`](https://clojuredocs.org/clojure.core/swap!)
Expand Down Expand Up @@ -125,7 +123,7 @@ The benefit of starting with simulations is they're free of messy unpredictabili

## Atomic in Action

Atomic has been used for developing and deploying to typical web hosts, Deno, Supabase, SharePoint, Cloudflare, and Power Apps and various production apps for years.
Atomic has been deployed to web apps/hosts, Deno, Supabase, SharePoint, Cloudflare, and Power Apps.

These examples model how one might write a program in Atomic:

Expand Down
21 changes: 21 additions & 0 deletions interactive-development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# The Interactive Development of Components

Interactive development makes programming a pleasure. Atomic wants to aid interactivity.

In any browser where the Atomic `cmd` module is loaded, enter `cmd()` into the console, to access your commands and components. Having imported `reg` from the `cmd` module into your modules, one may have used it to expose various commands and components/signals, beyond the standard library Atomic provides itself (`_`, `$`, and `dom`). Entering `cmd()` exposes into the browser console what modules usually keep private.

To expose changes happening against signals, append `monitor` to the query string of your web app.

* `?monitor=*` to monitor everything
* `?monitor=$state,$data` to monitor select signals
* `?nomonitor=$state,$data` to monitor everything but certain signals

Components are headless, stateful objects which provide input and/or output ports. These ports permit issuing commands and subscribing to events as well as wiring components together. A developer can gradually build up an app while directly and regularly interacting with it before all the right UI affordances are in place. Alternately, he can use this facility to monitor, debug, and/or extend an app.

This suits the Atomic philosophy.

Start with a pure, functional core and simulate what the app does. Then, wrap the core with an interactive shell, making it into a useful, side-effect producing component. From a turtles-all-the-way-down perspective, programs can be single components or component hierarchies. Then comes the UI.

In this way, apps emerge from cores, shells, and UIs. Normally, it's not possible to develop these areas one after the other. One may begin with a functional core and, soon enough, add a shell and/or UI. They are developed one after the other but also, to some extent, in parallel.

In practice, the shell and UI are usually combined into a single module and not necessarily worth the separation. The separation becomes worthwhile when your component warrants the sophistication of a command bus api, and the extensibility of adding things like decorators and handlers.
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
"scripts": {
"bundle": "rollup --config",
"develop": "rollup --config --watch",
"tests": "static ./tests",
"autopartial": "deno run ./tests/autopartial.js",
"autopartial-less": "deno run ./tests/autopartial-less.js"
"tests": "static ./tests"
},
"type": "module",
"engines": {
Expand Down
43 changes: 43 additions & 0 deletions placeholder-partial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Placeholder Partial

Pipelines and partial application are commonplace in functional programs. JavaScript is working toward [an operator](https://github.com/tc39/proposal-pipeline-operator) and [syntax](https://github.com/tc39/proposal-partial-application) but is overdue with its arrival. They can be had, of course, with Babel plugins ([operator](https://babeljs.io/docs/babel-plugin-proposal-pipeline-operator) and [syntax](https://babeljs.io/docs/babel-plugin-proposal-partial-application)), but this necessitates a build step, the very thing Atomic wants to avoid.

This is ideally what one wants to write:
```js
_.range(10)
|> _.map(_.pipe(_.str("Number ", ?), _.lowerCase), ?)
|> _.join(", ", ?)
|> $.log;
```
Atomic provides `chain` for writing pipelines:
```js
_.chain(_.range(10),
x => _.map(_.pipe(y => _.str("Number ", y), _.lowerCase), x),
x => _.join(", ", x),
$.log);
```
It's a good alternative to the pipeline operator, but since it's not [point free](https://en.wikipedia.org/wiki/Tacit_programming) it's downright ugly.
One can use partial, [as underscore does](https://underscorejs.org/#partial), but it's too verbose to be practical for routine use:
```js
_.chain(_.range(10),
_.partial(_.map, _.pipe(_.partial(_.str, "Number ", _), _.lowerCase), _),
_.partial(_.join, ", ", _),
$.log);
```
If you regularly write in the tacit style, your modules can be [repackaged](/dist/atomic_/) as shadow modules. These modules imbue exported functions with optional partial application via a feature called placeholder partial.
Add a trailing underscore to any folder exposing shadow modules to make them explicit.
With placeholder partial the tacit aesthetic is restored:
```js
_.chain(_.range(10),
_.map(_.pipe(_.str("Number ", _), _.lowerCase), _),
_.join(", ", _),
$.log);
```
7 changes: 0 additions & 7 deletions tests/autopartial-less.js

This file was deleted.

14 changes: 0 additions & 14 deletions tests/autopartial.js

This file was deleted.

0 comments on commit 3c034b5

Please sign in to comment.