Reactive Clojure: You don't need a web framework, you need a web language

Dustin Getz 2021 September https://twitter.com/dustingetz

See HN discussion: https://news.ycombinator.com/item?id=28630209 (178 comments)

Web data-sync doesn't compose

Let's define "data-sync contagion" as any web code a framework makes you write that isn't a functional expression. E.g. services, connections, sockets, reducers, normalizers, resolvers, etc. This imperative contagion is typically found adjacent to a network, and since it resists composition it tends to spread. Note that client/server/database web apps are characterized by pervasive network, and in fact most web application complexity is spent coordinating data flow between places in your system over network (e.g. client/server data sync).

To date, this coordination boilerplate has been abstraction-resistant. The essence of the problem is: How can you make a simple expression like below compose over network?

(reagent.dom/render

[:table (for [x (query-database ...)]

[:tr (pr-str x)])]

(js/document.getElementById "root"))

At Hyperfiddle, we think we've finally solved this.

Hyperfiddle Stack: a functional programming web stack that composes in datasync

- Miissionary: functional effect and streaming system for Clojure/Script

- Photon: ”Reactive Clojure/Script” implemented as a macro, with network distribution

- Photon-dom: fine-grained dom effects, server streamed. No React.js dep

- Photon-ui: robust distributed input controls with latency affordances

- HFQL: Hypermedia DSL for specifying CRUD/CQRS views. Reactive data sync

- RCF: async test macro with REPL-first ergonomics

Photon is the central piece of this stack. Photon has two big ideas:

1. "Reactive Clojure". Photon is a macro that compiles normal Clojure/Script syntax (s-expressions) to a dataflow signal graph (DAG). This DAG is lifted and further compiled into Missionary reactive signals. The result is that Photon s-expressions are reactive, like Excel or the React.js experience. Missionary manages reactive execution (incremental maintenance such that a small adjustment to inputs results in a small adjustment to outputs).

2. Distributed closures. Imagine a programming language runtime whose runtime state (i.e. lexical scope) is partially broadcast over network as it happens, kind of like a remote debugger. Such a language could replicate or mirror its processes across worker nodes in a distributed system, and keep them in sync with the real-time debug stream from the master. Now imagine the system can toggle which peer is the master node, thus transferring control flow over network, even right in the middle of a loop or deep closure. Photon achieves this.

Photon distributes reactive Clojure/Script programs across the client/server system (like distributed map reduce). Imagine React.js but full stack, incremental view maintenance all the way from database views to DOM views as one streaming computation. Functional programming teaches us how to achieve distribution with the full, undamaged composition power of lisp: higher order functions, closures, macros and all.

Photon is (will be) open source.

Key Insight

The point of Photon is to allow direct composition of query and view without data-sync contagion. With Photon, you can directly compose reactive dom views with reactive server queries:

(photon/defn query-persons [db needle]

(**server**

(datomic.api/q '[:find [?e ...] :in $ ?needle :where

[?e :person/email ?email]

[(clojure.string/includes? ?email ?needle)]]

db (or needle ""))))

(photon/defn render-persons [db needle]

(**client**

(dom/div

(dom/h1 "submissions")

(dom/table

(**server**

(photon/for [e (query-persons db needle)]

(let [{:keys [:db/id

:person/email

:person/gender

:person/shirt-size]} (datomic.api/entity db e)]

(**client**

(dom/tr

(dom/td (str id))

(dom/td email)

(dom/td (pr-str gender))

(dom/td (pr-str shirt-size)))))))))))

That means your views can query the backend as if the data was local. Even inside for-loops or conditionals, and at arbitrary nesting depth.

Sounds really slow and chatty right? Actually, NO!

This is not RPC or ORM. The key is to make the language, compiler and runtime in charge of the network, like the JVM owns the heap. Idealized client/server network IO (better than could ever be coded by hand) is an explicit design goal.

How does it work? Functional programming:

- photon/defn is a macro that compiles Clojure syntax (s-expressions) to a dataflow signal graph (DAG).

- The DAG is lifted and compiled into Missionary reactive signals. Missionary manages reactive execution (incremental maintenance such that a small adjustment to inputs results in a small adjustment to outputs).

- client & server are compile-time markers. The compiler (macro) colors the AST into client and server regions and identifies network transfer points.

- At network transfer points, the environment is serialized and streamed to the remote peer. This synchronizes the runtime state (i.e lexical scope) and enables distributed closures.

- So, Photon is a networked programming language. Write regular Clojure expressions, color them, and then the Photon compiler will give you distribution over the client/server distributed system for free.

How can compiler-managed network be efficient?

First, realize that the env is exactly what would normally have to be sent through the network in a REST API situation. There is zero difference in information content needing to be sent, it is simply inferred from the AST and managed.

How do we know the compiler will always find a desirable network topology without lots of toggling back and forth? This is deeply interesting, let's visualize the DAG:

Stunningly, the data flow partitions perfectly without superfluous network round trips!

Stunningly, the data flow partitions perfectly without superfluous network round trips!

This DAG reveals the essential structure of web apps: streaming, unidirectional data flow with minimized network transfer. The compiler uses knowledge of your AST to get the best network transfer automatically. (You know how every SAAS tool lags these days? We think managed network is the starting point to solve that by framing it as a compiler optimization problem.)

What are the types?

- Photon UI widgets are stream functions, from stream-of-data (reactive database queries and reactive views) to stream-of-events (reactive user interactions). The widgets read and write the DOM along the way by controlled side effect.

- All side effects are supervised by Missionary functional effect system and thus have an effect lifecycle (mount update unmount) with strong resource cleanup guarantees. For example, long-running database queries will be interrupted by update or cancellation signals, and DOM nodes gracefully cleanup.

- In the FRP spectrum, Photon is "self-adjusting computation".

Interactions and updates

A full Single Page Application actually has more than one network hop, crossing the network boundary back and forth in a number of ways:

- Lazy loading (such as popovers or select options) are simply additional regions of the DAG (intersected by network) that are guarded by an if statement.

- A server-filtered typeahead picker looks like a loop: the client sends user input to the server which touches the query and sends result back to the client (a full back-and-forth round trip). However the loop is async, it's not feedback, so the DAG is in fact acyclic.

In response to user interactions that trigger route changes, modals, infinite scrolling or other page reconfigurations, the DAG dynamically evolves ("self adjusting"), weaving in new branches and pruning old branches over time, in lockstep with the state of the application also evolving in time. (To visualize this, imagine the above DAG is animated.) This dynamic, stateful essence of a SPA is exactly modeled by a self-adjusting DAG and from this DAG the compiler will emit real-time netcode.

This stuff works

At Hyperfiddle, we have passing tests for this language, and since summer 2021, working web UIs. We figured it out about a year ago and have been working full-time on the implementation.

Photon is specifically designed to meet the sophisticated IO requirements of Hyperfiddle, our Clojure/Script library for rapid application development:

[Right click to open highres image in new tab] On the left is the Hyperfiddle hypermedia user interface DSL, and on the right is the live UI it produces. This is an imperfect mock, hopefully you get the idea. Hyperfiddle will generate a live web UI for any function from Clojure Spec metadata about its parameters. From this simple substrate of just functions and specs, the hypermedia DSL can orchestrate up to 50+ functions in a single page to form a full-bodied UI like this with deep and dynamic backend queries from any point.

[Right click to open highres image in new tab] On the left is the Hyperfiddle hypermedia user interface DSL, and on the right is the live UI it produces. This is an imperfect mock, hopefully you get the idea. Hyperfiddle will generate a live web UI for any function from Clojure Spec metadata about its parameters. From this simple substrate of just functions and specs, the hypermedia DSL can orchestrate up to 50+ functions in a single page to form a full-bodied UI like this with deep and dynamic backend queries from any point.

- The hypermedia UI DSL is implemented as a straightforward macroexpansion to Photon. It simply macroexpands to normal Clojure s-expressions which are then transparently lifted into Photon.

- Since Photon composes in datasync, our hypermedia/UI DSLs get client/server datasync for free, without needing to think about it.

- IO is streaming, backpressured, reactive, concurrent, real-time, collaborative. None of these runtime behaviors are specified in your code. Zero boilerplate. Free. It just works.

- Photon was designed specifically for this purpose of enabling Hyperfiddle. Hyperfiddle and Photon are jointly designed and developed by the same team.

- Photon is currently just 2000 LOC for the compiler, Clojure/Script runtime, and core library.

Designing for a specific use case is a powerful approach because it leaves no room for subjectivity with respect to the level of abstraction power. Hyperfiddle is the measure of how good Photon is. You make what you measure.

- Hyperfiddle-2020 is 18,000 LOC, + Reagent + React.

- Hyperfiddle-2021 is looking to be about 2000 LOC, + Photon + Missionary.

The measure of a Clojure codebase is how few LOC it is. Anyone else up for deleting 89% of their web codebase?

Conclusion

Reactive Clojure with distributed closures is the key primitive that erases client/server datasync boilerplate. When reactive web datasync is managed, all that is left is your logic, which is the functions and expressions that can be written and tested at the Clojure/Script REPL, as if you had a single-process system.

Here, we use the Clojure REPL to work with async Photon expressions:

Proof that Photon works at the REPL, with the help of hyperfiddle/RCF, an async test macro designed to enable REPL-driven development with async/streaming expressions. ✅✅✅ Async RCF will be released imminently for you to alpha test.

Proof that Photon works at the REPL, with the help of hyperfiddle/RCF, an async test macro designed to enable REPL-driven development with async/streaming expressions. ✅✅✅ Async RCF will be released imminently for you to alpha test.

Our goal with Photon is to let you express full-stack web applications as an expression, with zero boilerplate, zero framework. You have the REPL, lambda, recur, and the full undamaged composition power of Lisp. Thereby paving the way for a higher abstraction ceiling.

You don't need a web framework, you need a web language.

Tar pit

Clojure attracts people out of a belief that there is a better way, and yet every web application remains in the tar pit (in Clojure and every other language). Why? The frontend/backend false dichotomy. This is accidental complexity (incidental to the business) and yet we orient everything around it. Frontend framework. Backend framework. Frontend language. Backend language. Frontend team. Backend team. We ship the org chart, organizing around these accidental concerns that the business doesn't even care about.

React.js beat a false dichotomy by joining markup and logic into a single concern. Photon does that to the full-stack. The unifying abstraction is that of a functional effect system. Network is an effect. Rendering is an effect. Querying is an effect. Effects are asynchronous and concurrent. They can be sequenced and arranged into flows. They cost time to compute and response time matters. As functional programmers, this is the structure we study.

Does Photon bring us out of the tar pit? Certainly not and that's not the goal. The goal is to let you express rich client/server web applications out of lambda, so you can abstract higher. With Photon, IO is no barrier to abstraction.

Current Status: about 70% done.

- Photon and photon-dom are working. Streaming, distributed closures work. Database-connected dom views works. Interop with host Clojure/Script works. Macros work.

- Hyperfiddle-2020, the previous version of our product, has been live since 2020 with a production Series B funded business. This target ironed out all the real-world edge cases and the above stack is a full rebuild based on those learnings.

Remaining work:

- building out features in the hypermedia DSLs to reach parity with the Hyperfiddle-2020 production deployment

- Deploy it, find out whatever problems we have at that scale and deal with them

- User testing Photon with Clojure programmers. Learning curve may be an issue; that's a big part of why we're developing RCF (async test macro for REPL-driven development).

- Interactive/live-coding experience has not been started, but the stack sets it up to be straightforward. There are some unknowns.

How long will it take?

- We're releasing Hyperfiddle-2021 first. Hyperfiddle is the ultimate proof that this both works, and truly raises the abstraction ceiling. Also, as a declarative DSL it shelters you from changing internals while we work it out next year.

- Photon will likely be technical alpha for all of 2022 or longer (You might need to understand this stuff to get involved at this stage as the internals are not fully settled.)

Risks

- complexity ceiling

- leaky abstraction

- performance

- data security

- network failures

- will it grow fast

- social factors

- learning curve

- stack traces

- dynamic types

- typescript momentum

- wrong language

- finance

- What other risks do you see? Please let me know. If we can name them we can address them!

For updates, follow us at https://twitter.com/dustingetz, we have much more to talk about.

HN discussion: https://news.ycombinator.com/item?id=28630209 (178 comments)

Reactive Clojure: A web language (hyperfiddle.notion.site)

467 points by audionerd on Sept 23, 2021 | hide | past | favorite | 176 comments

delegate on Sept 23, 2021 | next [–]

This is not reinvention of PHP as some are commenting. In fact I think this is extremely cool.

If I understand it correctly, it allows you to achieve reactive data flow in a single page app without any boilerplate.

Meaning - you update the database on the server and all the relevant UI(s) will automatically receive the updated data and re-render only the parts of the UI that display that data.

This would require a ton of PHP and Javascript dealing with networking, websockets, routing, serializing data and so on.

Haven't tried it yet, but very curious to see if it works.

namdnay on Sept 23, 2021 | parent | next [–]

The problem with this type of system in my experience is that it’s great until you hit a bug, and then you realize you have no idea what’s going on under all the automagical stuff and you go crazy

manmal on Sept 23, 2021 | root | parent | next [–]

How is this different from any other framework? Try fixing a React or Rails bug.

At least their system is only 2k LOC, which will presumably be well documented.

christophilus on Sept 23, 2021 | root | parent | next [–]

At first glance, this appears to be much more complex. React is a relatively simple DOM diffing library. Rails is a closer comparison, especially with whatever their live-view implementation is. This appears to be doing a lot more than React, at least, including network communication, and database change event propagation. The odds seem much higher that stuff will go wrong, especially under load, and be pretty difficult to reason about.

Zababa on Sept 23, 2021 | root | parent | next [–]

> At first glance, this appears to be much more complex. React is a relatively simple DOM diffing library.

Their implementation is 2k lines of code. The React repo has 350k lines of code. The Rails repo has 336k lines of code (both of those according to tokei). Of course this includes tests, lots of other stuff, etc. But still, that's two orders of magnitude.

iaml on Sept 23, 2021 | root | parent | next [–]

Note that react has a monorepo with a lot of tangentially related packages, such as devtools and alternative renders.

Zababa on Sept 25, 2021 | root | parent | next [–]

That's true, it's hard to make a fair comparaison between the two.

cztomsik on Sept 24, 2021 | root | parent | prev | next [–]

preact is much simpler than react (~3KB), and whats weird, it's also faster

BTW: code is very easy to read/follow https://github.com/preactjs/preact/

steelbrain on Sept 23, 2021 | root | parent | prev | next [–]

LoC != Complexity. React has one job, and it does it really well, part of that 350k lines of code is a LOT of tests. Just because it has those tests doesn’t mean it’s “much more complex”.

My two cents :)

Zababa on Sept 23, 2021 | root | parent | next [–]

After a naive removal of the tests (every folder and files that contains tests was deleted), React still has 190k lines of code.

> React has one job, and it does it really well

I'm not sure I agree. It's hard to compare without a minimal copy of React to see how small it can get, but I'm guessing it could be an order of magnitude smaller. For example, React supports different ways of doing things (class components, function compenents, hooks). Backwards compatibility is a great thing, but it's not the same as "having one job and doing it well", it's a different tradeoff. The React team has lots of people that depends on their code, and thus choose stability over being small and nimble. I think that's a responsible choice. But this leads to complexity, and losing the "having one job and doing it well".

As a more general remark, there's a cycle in software. React starts small and nimble, especially compared to the "jQuery behemots of the past". It gets really popular. So people start depending on it. So it grows, and grows, and grows. And then someone else comes out, maybe Photon, maybe Svelte, maybe something else. Compared to React, it's small and nimble. Maybe in 10 years, Photon will be really popular, will be 200k lines of code, and someone will build an alternative because it's too big and complex.

Edit: as a more meta-remark: My message is way longer than yours because I was trying to steelman my argument, and you didn't do the same with yours. Considering you made a small message "defending" React and I did a long one "defending" Photon, I think we already agree about the tradeoffs involved here.

mhio on Sept 24, 2021 | root | parent | next [–]

Preact is React without the fluff - https://github.com/preactjs/preact

loc in a node project is hard to judge due to packages and the aforementioned mono repo difference, but the preact functional build artefact is an order of magnitude smaller: 129KiB v 8.2KiB

nickkell on Sept 23, 2021 | root | parent | prev | next [–]

I’m going to keep scrolling in search of a shorter comment

kitd on Sept 24, 2021 | root | parent | next [–]

Why?

pjgalbraith on Sept 24, 2021 | root | parent | prev | next [–]

Good point I checked and it's nowhere near 350k LOC. React is a monorepo here are some numbers for the main packages, excluding tests:

react = 3k

react-dom = 15k

kaba0 on Sept 23, 2021 | root | parent | prev | next [–]

It absolutely has a strong correlation with complexity.

ashtonkem on Sept 23, 2021 | root | parent | prev | next [–]

Clojure tends to be a bit more terse than other languages. 2K of Clojure is a lot of code. Especially if macros get involved. As for the comparisons to React elsewhere; I wonder how much of that is the result of React being battle tested for a long time and expanded, would this still be so short if Facebook was written in it?

I also wouldn’t presume good documentation as a general rule. That’s something that must be proven, not presumed.

manmal on Sept 23, 2021 | root | parent | next [–]

I‘d still rather read 2k loc in _any_ language, and undocumented, than 350k loc, perfectly documented.

ashtonkem on Sept 23, 2021 | root | parent | next [–]

Sure. But I’d question two things:

1) How often do you actually need to read your framework. I used rails for years and read probably less than 2KLOC out of it, ditto with Java and Spring. Good documentation beats out small code bases.

2) How much of the size difference between these two is down to age and uses? Is React so big because it’s unfocused or poorly written, or is that a consequence of it being used by so many people and projects? If it’s the latter, shouldn’t we expect that this project will end up growing if it got popular, eroding the benefit of its small size.?

MetaWhirledPeas on Sept 23, 2021 | root | parent | next [–]

> How often do you actually need to read your framework.

This brings us full circle to the question that started this conversation branch:

> it’s great until you hit a bug, and then you realize you have no idea what’s going on under all the automagical stuff

With React and Rails, "hitting a bug" is apparently rare. Maybe we should wait before assuming it will be any different with this Reactive Clojure framework.

tluyben2 on Sept 23, 2021 | root | parent | next [–]

In my (quite extensive) experience hitting a bug or another issue with react and rails and when/if the error message and trace are absolute drivel (which happens a lot during normal projects), you usually can find fix by pasting it in Google. In react it usually goes something like: throw everything away yarn/yarn build again and pray for the best by the way.

So I think many people don't need to read anything but just pop into Google read what it is + get the fix.

In smaller frameworks and libraries I am more tempted to just check what the issue is in the source and report a bug if it was not just me being stupid again, but that is also because pasting it in Google might get no results at all.

I remember when just starting out with Rails, my rails colleagues all read the source of everything when there were issues or lack of docs; this was very early on (first public version); now I know no one who does that anymore.

I really would like something that stays small and is around in 10 years and my experience with Clojure is good in that respect. And Haskel projects. Those things that keep growing and keep adding dependencies are nightmares but what can I do.

ashtonkem on Sept 24, 2021 | root | parent | prev | next [–]

The question is, are bugs rare in rails because of or in spite of the code size? Obviously more code is more opportunity for mistakes, but more code is also the product of more users and more contributors poring over what’s there.

baby on Sept 23, 2021 | root | parent | prev | next [–]

True With any system

simplify on Sept 23, 2021 | root | parent | prev | next [–]

That's true with any system you haven't sufficiently studied.

namdnay on Sept 24, 2021 | root | parent | next [–]

So yes in a way, but here we're talking about something that synchronises state between a client and DB over http. The possibilities of weird edge cases seem endless :)

giancarlostoro on Sept 23, 2021 | parent | prev | next [–]

So more like Liveview for Phoenix / Elixir is what this kind of sounds like to me, and less so back-end mainly language like PHP.

hk__2 on Sept 23, 2021 | parent | prev | next [–]

> Meaning - you update the database on the server and all the relevant UI(s) will automatically receive the updated data and re-render only the parts of the UI that display that data.

> This would require a ton of PHP and Javascript dealing with networking, websockets, routing, serializing data and so on.

I don’t know how it was implemented, but Quora had this back in its early days (~2012). There was some mechanism that "remembered" which table lines were used to generate which UI component, and when that data changed you had a live-reload in your browser. That was really cool to see at the time; I’d love to have more background on its implementation.

cabalamat on Sept 23, 2021 | parent | prev | next [–]

> you update the database on the server and all the relevant UI(s) will automatically receive the updated data and re-render only the parts of the UI that display that data.

I really don't like that idea. It seems to me inefficient and error prone.

Let's say you're updating a database. You add some records to one table, and you update some records to some other tables.

If the program automagically updates the UI, then on the first change it will attempt to update the UI (causing lots of processing) , then on the 2nd change to the database it'll update the UI again, and on and on for each change.

Wouldn't it be better to make all your changes to the database then only after that run an updatePage() funiction that updates the web page?

sooheon on Sept 24, 2021 | root | parent | next [–]

From submission:

"Sounds really slow and chatty right? Actually, NO!

This is not RPC or ORM. The key is to make the language, compiler and runtime in charge of the network, like the JVM owns the heap. Idealized client/server network IO (better than could ever be coded by hand) is an explicit design goal.

How does it work? Functional programming:

- `photon/defn` is a macro that compiles Clojure syntax (s-expressions) to a dataflow signal graph (DAG). - The DAG is lifted and compiled into Missionary reactive signals. Missionary manages reactive execution (incremental maintenance such that a small adjustment to inputs results in a small adjustment to outputs)."

agumonkey on Sept 23, 2021 | root | parent | prev | next [–]

nothing forbids the system to give mechanisms defining rapid sequences of changes, kinda like debounce in js

danenania on Sept 23, 2021 | root | parent | next [–]

I think the main risk for this kind of framework is that you spend as much time ironing out these kinds of "edge cases" (debounce, transition states, error states, transactions, authorization, url bar state, scroll bar position, paging, efficient re-renders, etc. etc.), which in practice are crucial to most non-trivial software, as you would have just writing the usual boilerplate and retaining fine-grained control.

It's great that people are innovating and creating new abstractions, and I'm sure there are apps where the tradeoff is worth it (internal CRUD-focused enterprise stuff comes to mind), but my knee-jerk reaction for a large app is that it will make easy things easier and hard things much harder.

noizejoy on Sept 23, 2021 | root | parent | next [–]

I appreciate and have created software that needed to be complex with the associated complex underpinning.

I also have long been looking for a framework that makes easy things easier.

agumonkey on Sept 24, 2021 | root | parent | prev | next [–]

Yes i agree

pizzeriafrida on Sept 23, 2021 | parent | prev | next [–]

This is where I'm at with it. "Develop apps like you do now, with a centralized app db, but now the app db (in the browser, most likely a giant Map) is an actual database with a sophisticated query language, an upgrade!"

hatch_q on Sept 23, 2021 | parent | prev | next [–]

In this sense it's same as what microsoft is trying to do with Blazor - except they are skipping whole javascript crap and compile to wasm.

brokencode on Sept 23, 2021 | root | parent | next [–]

Which sounds great until you experience the loading times. Last time I tried it out, the loading times for all the DLLs that a typical application requires was pretty bad. But once it loaded, things were snappy.

dmix on Sept 23, 2021 | root | parent | next [–]

I just tried this Blazor demo and its really slow on mobile, even after the noticeable load screen https://www.blazorfluentui.net/calendarPage

Clicking on multiple dates takes time to transition between the two.

Diggsey on Sept 23, 2021 | parent | prev | next [–]

> Imagine a programming language runtime whose runtime state (i.e. lexical scope) is partially broadcast over network as it happens, kind of like a remote debugger.

This sounds absolutely terrifying from a security perspective...

What's to stop a malicious client from broadcasting code that deletes my entire database?

outworlder on Sept 23, 2021 | root | parent | next [–]

> runtime state

> code

Some overlap, but these are essentially two different things.

> What's to stop a malicious client from broadcasting code that deletes my entire database?

Your backend.

Diggsey on Sept 23, 2021 | root | parent | next [–]

Deleting a database is probably a bad example, but my point still stands: even if you are not trasferring code back and forth, runtime state is enough to be a problem.

What if your runtime state includes an `is_authorized` flag or similar? How do you guarantee that this state remains server-side when the entire language conflates server/client side code?

For this to work, there needs to be language-level support for distinguishing untrusted inputs from trusted ones, or else it's a recipe for disaster.

weavejester on Sept 24, 2021 | root | parent | next [–]

If the compiler is separating server-side code from client-side code, then it's also separating out symbol bindings as well. So it knows where symbols are bound, and where they are referenced, and it can use this to limit information flow.

For example:

(client

(let [token (get-client-token)]

(server

(let [username (get-username db token)]

(client

(dom/p "Hello " username))))))

The compiler can infer that token is defined on the client, then sent to the server, which in turn defines username and sends that back to the client.

It's the same system you'd use in normal server/client architecture, just inlined.

chitowneats on Sept 24, 2021 | root | parent | next [–]

Compile-time macros do indeed seem to be what provides the necessary client/server separation for sensitive data.

That does mean you are trusting the library to implement these macros correctly. In that sense, data security for these symbol bindings is a responsibility of the library, and therefore a risk, as is called out lower on the page.

Once the library is complete however, and a larger part of the community has been able to inspect it, this type of bug should not be an issue. It's one of the most fundamental concerns of the library.

BillyTheKing on Sept 23, 2021 | root | parent | prev | next [–]

any sort of networking sounds terrifying from a security perspective...

Unfortunately (or fortunately) it's also a corner-stone of computing in general

doteka on Sept 23, 2021 | prev | next [–]

One thing I admire about the Clojure community is audacious projects like this. While a ton of these seem to run out of steam, they certainly shoot for the stars and tend to come pretty dang close for a while.

For those working in Typescript, Blitz.js seems to do a great job at drastically decreasing the plumbing you have to write to shuttle data between Postgres and React. There’s also a ton of goodies like auth built in. From my first impression it’s the closest the JS community has ever come to a Django, and that’s very high praise in my book.

grncdr on Sept 23, 2021 | parent | next [–]

It's a very impressive project, and something I would definitely try in a greenfield app.

I wish there was somewhere to follow updates that wasn't Twitter though.

Rd6n6 on Sept 23, 2021 | root | parent | next [–]

Especially for people without Twitter accounts

lerxst00 on Sept 24, 2021 | root | parent | next [–]

Try this https://fraidyc.at/

Rd6n6 on Sept 24, 2021 | root | parent | next [–]

Interesting, thanks

trutannus on Sept 23, 2021 | parent | prev | next [–]

Sadly the running out of steam effect hits the tooling for the community too. It's really hard to find good, stable clojure tools on the level of those available for Java.

gleenn on Sept 23, 2021 | root | parent | next [–]

I had another coworker compare Clojure and Java tooling recently. I just don't think it's fair to compare one of the most used languages on the planet to one that is written in the other one. Clojure tooling isn't the best out of some ecosystems I've used, but you can leverage a whole bunch of the Java tools and I certainly have, like stack dump analyzers or profilers, or Maven, etc. Are they perfect for use with Clojure, not always, but things are pretty good IMHO. Cursive is an excellent IDE too, a joy to use with very many bells and whistles and there is also excellent Vim and Emacs support. There are at least 3 build tools that are Clojure-specific (Leiningen, Boot, and deps.edn), or you can integrate with older or other JVM tools pretty easily like Maven. There are multiple testing frameworks, multiple test formatters, and multiple test runners. I could go on, but what specifically do you think is missing or could be better?

heyzk on Sept 23, 2021 | root | parent | prev | next [–]

Definitely take a look at Cursive, it may be what you're looking for along the lines of stability and out-of-the-box features you'd see in a Java IDE: https://cursive-ide.com/

LandR on Sept 23, 2021 | root | parent | next [–]

Or Calva for VS Code.

It's the best dev environment I've used for any language. It's brilliant.

trutannus on Sept 23, 2021 | root | parent | next [–]

Calva always exploded on me when I tried to set it up on Windows 10. Is it better on a *NIX system?

ziftface on Sept 23, 2021 | root | parent | next [–]

Never tried it on Windows, but it works pretty well on mac in my experience

trutannus on Sept 23, 2021 | root | parent | next [–]

I'll try it out on a Linux device, thanks!

lemper on Sept 24, 2021 | root | parent | next [–]

it works really fine on Linux.

LandR on Sept 28, 2021 | root | parent | prev | next [–]

I find it works great on linux.

On Windows I use it with WSL2 and its works just as well as on linux IME.

pkage on Sept 23, 2021 | prev | next [–]

This reminds me of Meteor.js[0] from back in the day (2014?), which had a very similar approach—you wrote code that ran on both the frontend and the backend, and database updates were propagated automatically. It suffered from a pretty hard reliance on Mongodb and its own package manager (Atmosphere), and it was at odds with the rest of the JS ecosystem which was settling on NPM.

This project looks very cool! I like the focus on composition, Meteor was lacking that (and really, most other frameworks do as well).

[0] https://www.meteor.com/

svachalek on Sept 23, 2021 | parent | next [–]

Yeah Meteor was an interesting example of an amazing project that fell into a hole based on a couple of bad decisions. It's basically fixed those at this point but become something pretty different in the process. It sounds like it may be something I can use now but I haven't tried it.

strictfp on Sept 23, 2021 | parent | prev | next [–]

Another big problem was that you rarely want exactly the same code on client and server.

basch on Sept 23, 2021 | parent | prev | next [–]

one that sort of fell by the wayside when Meteor and Ember got popular, was/is Derby.js/ShareDB. https://github.com/derbyjs/derby https://derbyjs.com/ https://github.com/share/sharedb

The LiveView lead resurgence in server side rendering is exciting. Does anyone have any insight as to why ShareDB never really took off?

cjblomqvist on Sept 23, 2021 | root | parent | next [–]

Having been fairly close to the Derby.js project I'd guess that the difference between it and just using React and a web API just isn't that much in reality. Live updates of data isn't something most applications need (and very few parts of those that actually do). The added complexity is not to be underestimated, and there are some things inherit with live data which makes it more complex. What happens when one piece of data updates at an unexpected point in data, half-way through a method where you process something? Plus you still have the difference between backend and frontend, just that now it's more difficult to understand where and when what is run where under what conditions.

Then specifically for Derby/Share JS they didn't put enough resources into the project to make it good enough compared to the alternatives.

zerr on Sept 23, 2021 | parent | prev | next [–]

And http://opalang.org/

daxfohl on Sept 23, 2021 | root | parent | next [–]

The language itself was a really interesting halfway point between typescript (near JS syntax) and purescript (stronger type system), before either existed. Plus some extra goodies thrown in.

It's really too bad they bound it so tightly to the framework, as I think there's a chance it could have succeeded as a language in itself. But these reactive shared-code things never seem to work out.

leetrout on Sept 23, 2021 | parent | prev | next [–]

I feel like Meteor lost a lot of ground to Firebase.

I think what we are seeing with these tools to make data synchronization in the frontend more invisible will continue to proliferate.

I am looking forward to the next, rich landscape of interactivity on the web powered by WASM, WebGL, etc.

FractalHQ on Sept 23, 2021 | root | parent | next [–]

> I am looking forward to the next, rich landscape of interactivity on the web powered by WASM, WebGL, etc.

All of which will likely be a broken mess on iOS thanks to Apple

aroman on Sept 24, 2021 | root | parent | prev | next [–]

i think you mean WebGPU rather than WebGL. also WebTransport will play a big part for truly real-time interactive web apps

leetrout on Sept 30, 2021 | root | parent | next [–]

Sure thing. Thanks.

zaksingh on Sept 23, 2021 | prev | next [–]

So from what I can tell this is conceptually similar to the LiveView model, except that the problem is approached from the perspective of the SPA/client stack instead of the SSR stack. It’s a refreshing idea that I’ve never seen executed like this before.

However I think it’s missing the point of de-coupling. Security would be very hard to reason about, as would handling of intermittent network connections when the real structure of the client and server are abstracted away from you.

Ultimately I think GraphQL with live queries is the best model for this type of reactive work. You get a decoupled client/server, reactivity, support for mobile clients as you have an API, as well as full type-safety on the client.

Nonetheless I applaud the creativity on display here and I hope I’m proven wrong. Maybe this will be the next paradigm shift? Who knows

zero_iq on Sept 23, 2021 | parent | next [–]

Mr first thought reading the article was security. After a quick ctrl-f, it was a little disconcerting to find that the only mention of security is in a tacked-on bullet list under the heading "risks"...

chii on Sept 24, 2021 | root | parent | next [–]

why is this any different from any other web api security concerns?

simongray on Sept 24, 2021 | root | parent | next [–]

When I edit Clojure I only ever think about indentation: https://shaunlebron.github.io/parinfer/

taikahessu on Sept 23, 2021 | prev | next [–]

This is exactly what I've been thinking of lately. I started web development in 1998 and in 2000 I got searching for some way to store data.

At first I tried Macromedia Coldfusion, later acquired by Adobe. Now that I checked on it, it seems to be going strong, to my surprise. It was too hard for me. And it was closed software. There was no way for me to learn it without spending money on it. And it wasn't what I was really looking for.

But I just needed something simple. Something to tinker with. So I found PHP. It was exactly what I needed at the time. Later I also found MySQL.

The amount of garbage required to build a single website is enormous. So enormous that we have gone a full circle and people start using static site generators to create pure HTML sites. Because of speed and few other reasons.

So I got to thinking, why is it that we're building webshops with all these open source technologies, with a huge amount of overhead and "bloat", when all you really need is a few simple things.

Well, as the creator said it, there are a lot of unknowns. Huge learning curve etc. But that's how Linux got started, as a tinkering platform. I really think this is the right path to take. Making an open source web programming language, that handles all the needs directly built-in. I totally agree with the philosophy and if you will, proposed abstraction, of the problem at hand.

But also, it makes me shiver to look at the code and not understand it. So much to learn. But it gives me hope to see, that other's have come to the same conclusions. Looking forward to hearing more!

Zelphyr on Sept 23, 2021 | parent | next [–]

Clojure (and Lisps in general) really aren't hard to learn. It just takes those of us that come from C-influenced languages like PHP a little longer to be able to read them at a glance because the syntax is slightly different and usually simpler.

Once that lightbulb of the power of a Lisp goes off, there's no turning back. As Eric S. Raymond said, "Lisp is worth learning for the profound enlightenment experience you will have when you finally get it; that experience will make you a better programmer for the rest of your days, even if you never actually use Lisp itself a lot."

bcrosby95 on Sept 24, 2021 | root | parent | next [–]

The best tip I ever got for reading Clojure was to ignore the parens and look at the indentation.

iainctduncan on Sept 23, 2021 | root | parent | prev | next [–]

If there was one and only one thing I could tell myself from 15 years to do sooner it's LEARN SCHEME. Man I wish I'd stated a decade ago.

Fellshard on Sept 23, 2021 | prev | next [–]

Quite a few comments that are dishonest or didn't focus on what the author focused on, but rather on auxiliary things.

There are concrete similarities to PHP, which is effectively what this server/client macro setup gives you at a superficial level. However, from what I'm seeing, this will behave more like a LiveView or Hotwire, with the focus being on optimizing network requests in an automated manner.

My big question with the tool is this: if you're passing environments back and forth, how secure can this feasibly be? Is there an automated limit on what will be considered based on the generated code? How do you handle malicious environments?

mamcx on Sept 23, 2021 | prev | next [–]

This is tangential but also a validation.

FoxPro (& dBase) is a realization of the concept: To deal with databases, you need a database language.

Exist a lot of minor things that our apparent "general-purpose" languages lack in the moment you need to deal with certain niches. From very small stuff as not-even available decimals, dates, currencies, units types, to lack of simple way to transform data, to ad-hoc queries, to ad-hoc data validations, to lack of relationship modeling, etc.

Even if you say "linq!, ActiveRecord!, functional!, lisp!, pandas!..." and others all that are a shadow of what the dBase family provides.

How far? I was not in worry about all the stuff everyone worry about today (injection? orms? impedance mismatch? reactivity? <- an over-complicated patch on top of unfit languages for it, so kudos for this idea!). That is what make me put some time aside in build a language in the spirit of it, because is so much details that are not available if the languages is not designed with data(as of the kind of business) in mind.

So, in short, most languages, even php, python, ruby, ... are not that good for web programming (and worse for database programming!), just that are not that terrible, either.

avanai on Sept 23, 2021 | prev | next [–]

When the framework does so much for you, I always worry about what it looks like at scale when you need to start optimizing. Can you instrument all the plumbing, if something breaks can you get at it to fix it? How will you add caches at various different layers? On the browser, http cache, db cache, CDN, etc.? If the generated JavaScript that’s ultimately running on the browser has a bug, how many layers of library do I have to sift through to fix it? When I want to simulate the network for testing or deal with intermittent connection failures on the frontend how hard it is to plug in? What do schema migrations look like? What do deployments across a fleet of servers look like (there’s some point in time when some servers are old and some servers are new…)?

These are problems with any framework, but the more all-in-one a framework attempts to be the harder it is to get in between the joints with your glue gun to fix things up.

That said, this is Clojure and usually you have pretty easy access to all the intermediate bits and bobs and macros so maybe it’ll be great.

lewisjoe on Sept 23, 2021 | prev | next [–]

This is beautiful. I used to think about this problem deeply. The best imaginary system I did imagine was:

- A system that starts with our database schema

- a language in the front-end that abstracts away server connection and db access

- this imaginary language should allow defining react-like components but treat the db as a local datastore

- Most clients are UI stricture interpolated with that user’s data queried from the central db. So this imaginary front-end language should allow querying from user-level views from the db

Most of what I read from the post, looks like a realization of this dream.

FpUser on Sept 23, 2021 | parent | next [–]

>"- A system that starts with our database schema

- a language in the front-end that abstracts away server connection and db access"

Optimal data layout for storage and for processing / presentation can be quite different. Automatically mapping one to another I think can not be efficiently implemented in automatic fashion. I've tried different frameworks that claim to achieve it but at some point you always hit the wall. As a result I've long abandoned all those attempts and do manual transformation in code that are optimal for my particular situations

hatch_q on Sept 23, 2021 | parent | prev | next [–]

You need to treat clients as zero-trust. Your db is just a vector for DOS attack if you don't.

intrasight on Sept 23, 2021 | prev | next [–]

It had me at the title "You don't need a web framework, you need a web language" but then it immediately dived into arbitrary syntax instead of exploring in abstract what a modern web language would entail.

isuckatcoding on Sept 23, 2021 | parent | next [–]

Yeah I had a really hard time understanding even with the backend frontend highlighting

balloneij on Sept 23, 2021 | root | parent | next [–]

I think the misunderstandings are because they are targeting a specific audience. They aren't trying to teach you Clojure, Datomic, and Reagent because they assume you are already knowledgeable about it.

Clojure and Clojurescript are the same language (more or less). They just target the JVM or Javascript.

The author is mixing the frontend (Clojurescript and Reagent) code with the backend (Clojure and Datomic) code in the same expression. Then through their magical system and the beauty of lisp, they pull the frontend and backend parts out to serve them separately.

pizzeriafrida on Sept 23, 2021 | prev | next [–]

It does seem a bit convoluted after going through it a couple of times, but I'm down to try it if my reagent React interop still works, e.g. `[:> MyJsComponent {...}]`.

I would say that I am very happy with the FE stack of reagent / reframe at the core. I have long chased the dragon of co-located queries ala GraphQL instead of basic re-frame subscriptions, or redux.connect and pulling fields off a map. Obviously having the ability to be more expressive with data queries is great, but in reality I have come to settle on basic subscriptions into maps, syncing data into my db via events. It's not super pretty but it scales!

This seems like it's trying to push the needle, and I will it.

pgt on Sept 23, 2021 | prev | next [–]

The Web After Tomorrow beckons: https://tonsky.me/blog/the-web-after-tomorrow/

Well done, @dustingetz!

awinter-py on Sept 23, 2021 | prev | next [–]

'compiler managed network' in here is a useful concept

buildsystems are increasingly doing the heavy lifting of telling backend how to deliver a pre-hydrated frontend, and telling frontend how to speak backend's language

would be nicer if this was just types and schemas, so you didn't have to use a full-stack framework to get full-stack accelerations and linting

marstall on Sept 23, 2021 | prev | next [–]

As a web developer with 25 years of experience and (I thought) up to the minute skills ... I have no idea what any of that means but it sounds cool and important and I want to try it.

pjlegato on Sept 23, 2021 | parent | next [–]

It allows you to define a GUI / DOM specification, with transparently embedded serverside commands that do things like "query the database for the list of users who meet criteria X." No annotation or other special action required.

The runtime figures out an efficient and reactive way of parceling out work to the server as needed, and refreshing it only when necessary.

nickkell on Sept 23, 2021 | parent | prev | next [–]

Hats off to the lispers, they do some cool stuff. I think treating code as data they just have a different view of what computation means to the rest of us (or to me at least!)

quickthrower2 on Sept 23, 2021 | parent | prev | next [–]

You don’t have to know everything you see. If you find it interesting then cool but don’t feel compelled to learn every web thing, it’s a never ending rabbit hole.

hungryforcodes on Sept 23, 2021 | parent | prev | next [–]

More work and learning basically in "front end" land, before you can do anything practical -- as usual.

bcrosby95 on Sept 23, 2021 | root | parent | next [–]

I view it as an attempt to reduce the work required in "front end" land, making full stack development more feasible than it is today.

Kaze404 on Sept 23, 2021 | root | parent | prev | next [–]

Because software engineers are known for being people who are averse to learning and trying new things out.

hungryforcodes on Sept 23, 2021 | root | parent | next [–]

As opposed to actually spending their time building useful practical things?

banana_maker on Sept 23, 2021 | root | parent | next [–]

Yes, that's why I manually input 1s and 0s - no time wasted on learning, only outputting practical things.

Kaze404 on Sept 24, 2021 | root | parent | prev | next [–]

I hope you don't live every second of your life building useful practical things.

hungryforcodes on Sept 24, 2021 | root | parent | next [–]

I do actually. What else are you supposed to do with your time? Life is short, best to use it.

Kaze404 on Sept 24, 2021 | root | parent | next [–]

I'd say commenting on Hackernews isn't building something, let alone something useful. So that's at least one thing you do that's not that.

hungryforcodes on Sept 26, 2021 | root | parent | next [–]

Everyone needs a break from time to time. :P

jazzyjackson on Sept 23, 2021 | root | parent | prev | next [–]

does nothing practical happen on the front end?

throwamon on Sept 24, 2021 | prev | next [–]

Interesting how no one has mentioned Fulcro. There seems to be a lot of overlap between the two. Anyone who has used both and mind comparing them?

https://fulcro.fulcrologic.com/

sooheon on Sept 24, 2021 | parent | next [–]

Yeah Fulcro has been building up their implementation of these ideas for ages, and used in anger, I think most famously by nubank.

From https://book.fulcrologic.com:

"The core ideas are as follows:

Graphs and Graph Queries are a great way to generalize data models.

UI trees are directed graphs that can easily be "fed" from graph queries.

User-driven operations are modeled as transactions whose values are simply data (that look like calls).

Arbitrary graphs of data from the server need to be normalized (as in database normalization):

UI trees often repeat parts of the graph.

Local Manipulation of data obtained from a graph needs to be de-duped.

Composition is King. Seamless composition is a key component of software sustainability."

ziftface on Sept 24, 2021 | root | parent | next [–]

Am I wrong or is fulcro frontend only though? You wouldn’t get access to any server resources so you still need to manage syncing state with the server. Or am I wrong about that?

sooheon on Sept 24, 2021 | root | parent | next [–]

Yeah you were mistaken, it's a full stack design. They recommend https://github.com/wilkerlucio/pathom for the connection. It's GraphQL done right, you write data "resolvers" on the backend, you declare very flexible graph queries on the front end -> this populates client DB and then fulcro uses that to render frontend.

Highly recommend reading through this section: https://book.fulcrologic.com/#FullStack

atroche on Sept 23, 2021 | prev | next [–]

How does the reactivity work with Datomic? Is it polling those queries? Or I’d it listening to the log of transactions and figuring out when relevant parts have changed?

athenot on Sept 23, 2021 | prev | next [–]

This reminds me a lot of imba[1], which mixes the front end and the back end. Though ReactiveClojure seems to be more about reasoning in terms of a lisp for all parts of the app, whereas imba is more focused on reducing syntax noise as much as possible.

[1] https://imba.io

Datsundere on Sept 23, 2021 | prev | next [–]

I think people are overlooking the fact that javascript and css is a hot mess to deal with. We need something like flutter that makes maintaining these easier.

rlili on Sept 23, 2021 | prev | next [–]

Coincidentally, Alephjs (https://github.com/alephjs/aleph.js) added a commit hours ago that also seems to solve this particular problem for React.

So now there's a React hook (useDeno) that takes a callback that is only executed on the server-side, and the returned value is sent back to the client side transparently.

fny on Sept 23, 2021 | prev | next [–]

I think I fail to see the advantage in the example given. Is the point that there's a server side call attached to the UI component directly? How is this any different than what can already be done with old school full stack like Rails or even smaller libraries like Sinatra/Flask.

tambourine_man on Sept 23, 2021 | prev | next [–]

If you are trying to allure front end devs, maybe don't use tables in you example.

To my taste, all of that is way too overcomplicated. I don't know why we need to make writing and maintaining web pages more complex with every year that goes by. To my mind, this industry looks completely derailed.

specialp on Sept 23, 2021 | parent | next [–]

What would you use to represent tabular data like a list of T-shirt orders as in the example? That should be an HTML table. Using tables for positioning is bad in front end. Using HTML tables/sections/headers for document data structure is exactly what HTML is for.

tambourine_man on Sept 23, 2021 | root | parent | next [–]

Unless it's two columnns only, tables don't work well on mobile. You end up having to undo in CSS a lot of properties that TD, TH bring by default.

disease on Sept 23, 2021 | prev | next [–]

My arrow and page up/down keys do not work on this website. Very annoying.

cutler on Sept 24, 2021 | prev | next [–]

"Photon will likely be technical alpha for all of 2022 or longer". Wake me up in a couple of years then.

tosh on Sept 23, 2021 | prev | next [–]

great to see hyperfiddle here, delightful surprise, deserves the attention, kudos @dustingetz, ty for the writeup

jack_riminton on Sept 23, 2021 | prev | next [–]

Can someone translate this for a relative web dev noob? or can anyone? which may be a better question

aidenn0 on Sept 23, 2021 | parent | next [–]

A web app includes code that runs on the client and code that runs on the server. For example, code that manipulates the view runs on the client and code that accesses the database runs on the server.

This lets you write a function where some of the code runs on the client, some of the code runs on the server, and the compiler figures out which and emits the network RPC calls for you.

metalrain on Sept 23, 2021 | prev | next [–]

I like how client and server code is separated, but I do wonder how changes in expressions are evaluated.

I'm thinking about autocomplete that on new user input (needle='ad') filters previous result from server (needle='a') in the client before server returns a new response from new input (needle='ad').

Essentially can inner parts of expression update even when they are somewhat dependent on reactive data from server that comes from their parent expression?

terracottage on Sept 25, 2021 | prev | next [–]

This is very cool, and ambitious, but i have one major objection: the claim that web apps are an about unidirectional data flow from a managed data store to a consuming front end.

That's not the kind of apps I want to build. I want workspaces where I can make and edit and work freely. I don't care to be online to do it, and conserving bandwidth is not a constraint that should define how I use it.

The DAG goes from me to me.

bennyp101 on Sept 23, 2021 | prev | next [–]

Lots of people saying it’s like PHP or liveview with elixir.

We kinda have that with livewire[1] and inertia[2] and as awesome as they are (no separate api etc) they also suffer from the “magic”

[1] https://laravel-livewire.com/ [2] https://inertiajs.com/

turtlebits on Sept 23, 2021 | prev | next [–]

Off topic, but please don't use Notion for static sites. The page is 1.8MB (9.4MB uncompressed) - 1.6MB of that is javascript.

nbardy on Sept 23, 2021 | prev | next [–]

This is great. It's the dream that was promised 4-5 years ago of with om and reagent fully realized.

nephanth on Sept 24, 2021 | prev | next [–]

How does this compare to frameworks that abstracts over network calls such as ocsigen/eliom [1] ?

[1]: https://ocsigen.org/tuto/latest/manual/application

cies on Sept 23, 2021 | prev | next [–]

I think no-code/low-code backend is here to stay. Products like Hasura put authorization/ratelimiting/graphql/api-GW on top of trusted postgres. Now BE and no serialization up and down. Simply generate a client based on the graphql schema and off you go.

jtolmar on Sept 23, 2021 | prev | next [–]

I've thought of doing this several times, and always shied away at the complexity and nuance that goes into it. Kudos to the devs for having the skill and determination needed to actually make this real. Very impressive work, and I hope it works great in practice.

rowland66 on Sept 23, 2021 | prev | next [–]

I think that several of the fallacies of distributed computing are being ignored here.

https://en.wikipedia.org/wiki/Fallacies_of_distributed_compu...

telekid on Sept 23, 2021 | parent | next [–]

Which ones? And ignored (and potentially irreconcilable) or deferred?

gdsdfe on Sept 23, 2021 | prev | next [–]

I'm a bit confused, is this a product or a project?

danpalmer on Sept 23, 2021 | parent | next [–]

Yes.

danpalmer on Sept 23, 2021 | root | parent | next [–]

To expand on the snarky response, or explain my thinking...

I think more and more the lines are blurred between open source projects, and products. This is good and bad.

It's good because people making money out of open source projects probably means more open source projects, more support available, and a healthier tech industry.

It's bad because as someone with no intention of turning a few open source libraries into a full time job, there's still an expectation of a certain level of polish to them that makes more sense for products. Open source projects with clever names, logos, mission statements, a domain name and marketing site/landing page, marketing copy, flashy documentation, a live preview environment, etc. These are all a lot of work for an open source project, but the stakes are raised to this level by the productised open source projects that can afford to fund this sort of thing.

gdsdfe on Sept 23, 2021 | root | parent | next [–]

I get that and I agree with you but then if it's a project why do I need to ask for early access, if it's a product then why launch a web tech on notion? one would expect to build the website with the so called web language and have the proof in the pudding like they say.

amw-zero on Sept 24, 2021 | prev | next [–]

I’m very into stuff like this, and I really can’t make heads or tails of what this post is about. There’s a ton of hand waving, and a ton of words, but why not use them to explain exactly what this thing is doing?

buryat on Sept 23, 2021 | prev | next [–]

looks very overcomplicated for generating an html table, building something more powerful would result in even more complex code. There's a reason why these things get decoupled

paulgb on Sept 23, 2021 | parent | next [–]

> There's a reason why these things get decoupled

This is true, but I also think it's worth evaluating whether the lines along which we've been decoupling applications is the right one. Typically, the line of demarcation has been the client/server boundary, for a bunch of reasons: security considerations (it's also a trust boundary); different computational environment. This split has reinforced itself with the organization of companies into frontend/backend teams.

But there are a bunch of things that it makes a pain-in-the-ass. I've encountered this most with data-heavy apps, where I want to do some analysis on the server (e.g. Python/Pandas), but I want low-latency recalculations on the client in cases where the data is small enough. Doing it “right” requires implementing the same data-level logic in both Python and Javascript. Nobody has time for that, so we end up in our current world of laggy janky SaaS that needs to run off to the server every time you click a button.

Which is to say, I'm excited that people are looking at alternatives (not just this; LiveView was on HN yesterday as well).

mjburgess on Sept 23, 2021 | prev | next [–]

Finally the problem which PHP solved more than two decades ago has been solved again. How do we include server-side code in client-side code?

Do we build the client-side on the sever, and render? Why no, that would be PHP.

Let's build the client-side in the browser, and rig a series of complex code-generating primitives disguised by the beauty of a language, to AJAX our way to a presumably good-enough solution.

I think its a serious question whether apps built with this are more performant, easier to use, (and so on) than the equivalent PHP-approach.

tomconroy on Sept 23, 2021 | parent | next [–]

Claiming "this is PHP" misses the point entirely, which is turning PHP-like templates into apps that can react to user input

Zelphyr on Sept 23, 2021 | root | parent | next [–]

There has got to be a better way of achieving reactive templates than what is described in this project though.

theptip on Sept 23, 2021 | root | parent | next [–]

I think Phoenix LiveView (posted recently on HN: https://fly.io/blog/how-we-got-to-liveview/) and Rails Hotwire (https://hotwired.dev/) are the main alternatives that have momentum right now.

The general idea being to abstract away the difference between client and server code, so that you can write code that handles both, in a single file.

> There has got to be a better way

What's offensive about the OP to you? Is it just that you don't like functional programming / Clojure (fair, that's ultimately a matter of taste / aesthetics at some level)? Or is there something about the technical implementation you think is suboptimal?

m_floer on Sept 23, 2021 | root | parent | next [–]

Thank you for posting those two, I wanted to post them but I don't comment often. Wanted to chip in another contemporary: edelvalle/reactor, which is inspired by LiveView[0].

[0]: https://github.com/edelvalle/reactor

I am using Hotwire for a project, and I'm learning Elixir and Phoenix on the side. Finding edelvalle/reactor was immediately helpful to me though, because I cut my teeth on Python/Django, so reading a Python reference implementation helps me learn nuts and bolts of libraries, faster. (so, I figure that this might help someone else grok how these approaches work.)

theptip on Sept 23, 2021 | root | parent | next [–]

Thanks for sharing - I currently work with Django so I'm very happy to see an equivalent library in that ecosystem.

Have you found any issues with Reactor, areas where it's behind Hotwire, etc?

Spivak on Sept 23, 2021 | root | parent | prev | next [–]

I mean sure but you can’t get over the need to have a two way pipe of some kind to pass events server <-> client. Like no matter what you’ve got two balls of state with bidirectional relationships between them.

runawaybottle on Sept 23, 2021 | root | parent | prev | next [–]

What if we put a DOM API into php with a few state hooks? Does that solve it? Because the GP is right, this is PHP.

dgb23 on Sept 23, 2021 | parent | prev | next [–]

PHP doesn't reactively and selectively propagate changes between client and server, nor would it be feasible to write such a library in PHP itself. You could maybe achieve something similar with code generation of PHP and JS.

slim on Sept 23, 2021 | root | parent | next [–]

But are you sure propagating changes is desirable ? It looks like a different architecture than the web to me

dgb23 on Sept 24, 2021 | root | parent | next [–]

The use-case here is that the interface is highly interactive and coordinated. The suggested approach and library tries to blur the line between the logical components of client, server and database. Whether that is desirable depends entirely on what the assumptions of your web application are.

tored on Sept 24, 2021 | root | parent | prev | next [–]

You can use SSE with PHP to propagate changes.

dgb23 on Sept 24, 2021 | root | parent | next [–]

That would be a building block to achieve something similar.

Implementing this would however smear your logic all over your client and server and require careful plumbing. SSE doesn't come without caveats either.

The suggested approach here is to abstract away from that plumbing and move to a more declarative, composable expression. Whether you want that or not really depends. I see this as a specialized paradigm to solve a specific category of problems.

tored on Sept 25, 2021 | root | parent | next [–]

You can avoid that just using SSE to notify (no data) the frontend to rerender, you can still render everything in the backend.

darthrupert on Sept 23, 2021 | parent | prev | next [–]

Hate towards PHP has absolutely nothing to do with how easy web things are to deploy on it.

cosmotic on Sept 23, 2021 | parent | prev | next [–]

There's theoretically some performance benefit to rendering client side over ajax because the pages can be trivially cached, though you then run into the issue of pages being placeholders for a moment while the subsequent request(s!) run. Hence the UX anti-pattern of skeleton screens.

tshaddox on Sept 23, 2021 | root | parent | next [–]

I don’t care for skeleton screens much in any case, but there’s no reason why skeleton screens couldn’t be limited to client-side page loads while the client waits for the data. The server could and should still fetch all the data and send down a fully-rendered page.

Zelphyr on Sept 23, 2021 | root | parent | prev | next [–]

I will be so relieved when that anti-pattern goes away.

Zelphyr on Sept 23, 2021 | parent | prev | next [–]

I really love Clojure--it's a joy to use but, you're right.

Maybe I'm misunderstanding this project but it seems like abstractions on top of abstractions and has little to do with being a "web language". That was PHP, for better or for worse.

weavejester on Sept 24, 2021 | root | parent | next [–]

You're misunderstanding. The project is proposing writing client-side code and server-side code in the same expression, then having a compiler automatically separate the two parts out and determine how they should communicate.

kazinator on Sept 23, 2021 | parent | prev | next [–]

PHP lets you have a piece of server code that can see the local variables of the client code it's enclosed in? And it's all reactive?

That's amazing!

InitialBP on Sept 23, 2021 | parent | prev | next [–]

Regardless of whether PHP solved this particular problem, it also caused a lot of its own due to numerous security issues and documentation that encouraged unsafe use of libraries, until recently.

mjburgess on Sept 23, 2021 | root | parent | next [–]

The pragmatism of rasmus, and how bemused he was that people were using his language to program (vs. template), meant that the BDFL was not a great advocate of the PHP model.

The model is this: run-once-and-die + build-it-on-the-server.

Those two ideas are extremely powerful, a little like immutability, in that they rule out a huge number of issues. The issue with PHP wasn't this model which became associated with the morass of amateurs using the language. A shame.

BiteCode_dev on Sept 23, 2021 | prev | next [–]

We did that, it was called PHP, and it turns out DSL are pretty limited outside of their sweat spot. Also they don't evolve as fast as libs and framework.

Case in point: PHP worked well, but framework like Rails and Django became very attractive because their offered more, and used languages that could be good outside of their niche.

And what did the PHP community to stay relevant ? They developed great frameworks, and improved the non web language capabilities.

Turns out the web moves fast, and coding a web app is more than web programming anyway.

j45 on Sept 23, 2021 | prev | next [–]

Am I missing something or is reactive clojure a web framework for a language too?

I don’t recall Clojure being a web centric language like PHP, ASP, etc.

askonomm on Sept 23, 2021 | parent | next [–]

ClojureScript (JS target) is as web-centric as you can think, with what you can write front-end apps, Node.js apps, React and React Native apps, using Clojure.

Clojure (JVM target) can do back-end web as well as Java can, as well as anything else that Java can.

Clojure is a general-purpose programming language, so it can do pretty much anything.

zubairq on Oct 5, 2021 | prev | next [–]

Hi Very interesting read. What would be the possible business models for this to make money?

oaguy1 on Sept 23, 2021 | prev | next [–]

Hugged to death and I’m afraid there is no wayback archive right now.

haskellandchill on Sept 23, 2021 | prev | next [–]

This is nice but it doesn't give you a reasoning system for making assertions about components. I'd rather work backwards from that point to a web language but playing around with things dynamically is the most natural and explored model.

throwawayswede on Sept 23, 2021 | prev | next [–]

Not related, but is Notion becoming new Medium?

jspaetzel on Sept 23, 2021 | prev | next [–]

Loving this for you...

)))))))))))

askonomm on Sept 23, 2021 | parent | next [–]

Found the one person who uses Notepad.exe because no actual Lisper even notices the indents due to structural editing capabilities of IDE's, much like {} in C-based languages.

peter-m80 on Sept 23, 2021 | prev | next [–]

Thanks. I hate it.

baby on Sept 23, 2021 | prev | next [–]

They chose a parenthesis-language, interesting choice. I don’t think the syntax is going to attract many web devs.

pbiggar on Sept 23, 2021 | prev | next [–]

I'd love to know why any time I read something about clojure, it's inevitably cluttered with big words that the author (or some other clojurian) invented that have a lot of deep and important meaning, that make it impenetrable to anyone else.

greendream17 on Sept 23, 2021 | prev | next [–]

Premature compositionally in the root of all evil, or something like that. I've looking for better ways to do document.createElement and addEventListener ever since the days of DHTML. Anyone looked into diagram theory already? The only productivity gain I've found is being fluent enough in your stack of choice to do the work to solve a problem and nothing more. And sure, spend the rest of your time researching diagram theory, but try to be honest about why you do it.

Kinrany on Sept 23, 2021 | parent | next [–]

Premature compositionality? If something didn't need to be decomposed, it can be recomposed.

greendream17 on Sept 23, 2021 | root | parent | next [–]

As in function composition, a great idea from math but one that is hard to use for anything that isn't a functions with domain and range in R or N.

softfalcon on Sept 23, 2021 | prev [–]

This syntax is insanely hard to read, is non-standard, and is unclear what is server side and what is client side processed without purple and red highlighting.

Maybe I’ve been in Node.js land too long, but I don’t get why this is better for my productivity or my ability to create efficient web apps.

mrtnpwn on Sept 23, 2021 | parent [–]

It's not hard to read, but it's just that you aren't used to it. Try Clojure without this "hard to read" mindset and see how great Lisp as tools are!

softfalcon on Sept 25, 2021 | root | parent [–]

Definitely will give it a go when I have some free time!

Also, I can’t edit it right now, but would like to apologize for saying it’s “insanely hard to read”. That was rather harsh and uncalled for.