Electric (formerly known as Photon) is a reactive and network-aware Clojure/Script DSL that fully abstracts over client/server state sync at the programming language layer, in order to achieve strong composition across the frontend/backend boundary in dynamic web apps. With Electric, backend expressions (i.e. queries) and frontend expressions (i.e. views) compose directly. There is no incidental network divide. The Electric macros will, at compile time, perform deep graph analysis of your hollistic program's data flow in order to transparently partition and distribute it across the client/server distributed system. There is no client/server dichotomy from the programmer's perspective. All IO and effects are managed.
Figure: This is not RPC or client-side ORM. The Electric compiler performs deep graph analysis of your unified frontend/backend program to automatically determine the optimal network cut, and then compile it into separate client and server target programs that cooperate and anticipate each other's needs.
-
Fully reactive: unlike javascript frameworks, in Electric, reactivity is built directly into the programming language itself. Reactive-if, reactive-for, reactive try/catch. When everything is reactive, it feels like nothing is reactive. No observables! No async types! De-load your mind and relax.
-
Multi-tier: frontend and backend are defined in the same expression, same function, same file. It's not code sharing, it's code splitting. Let the compiler infer the boundary from your code, instead of contorting your code — nay, your entire architecture — to fit the boundary.
-
Network-transparent: Electric closures close over server and client scope bindings, all in the same expression. The Electric compiler uses compile-time static knowledge of your source code to slice your expressions into client and server portions. Right through closures, loops and deeply nested function calls.
-
Strong composition: Network-transparent Electric functions are true functions. They follow function laws and work at the Clojure/Script REPL. You have lambda, recursion, HOFs, closures, dynamic scope, macros, etc: the full undamaged composition power of Lisp. Goodbye "functional core imperative shell"; with Electric the entire system is a function.
-
Multiplayer-native: everything is automatically multiplayer, 0 LOC cost.
Our mission is to raise the abstraction ceiling in web development in the same way that garbage collection did for functional programming, paving the way for something new.
Demos
- Proof of client/server transfer
- TodoMVC
- TodoMVC Composed – network transparent composition
- Folder explorer: frontend table over node_modules
- Datomic database browser
- Infinite scroll
- Multiplayer chat with presence
- Recursive tree-view (my favorite)
- REPL
- Webcomponents
- multiplayer canvas as a stock component
Tutorials
- React.js/Reagent interop
- Two Clocks – a network stress test
- Full-stack webview
- Todos Basic
- Electric Y-Combinator: network-transparent composition
- SQL data backend
How it works
Scrap
First run the demos:
git clone git@github.com:hyperfiddle/electric.git
cd electric
yarn # optional, only needed for demo of React interop
clj -A:dev -X user/main # serves demos at http://localhost:8080
From the REPL:
dev
alias;(user/main)
compiles assets and serves app.- see src-dev/user.clj & user.cljs
- demo source code at src-docs/user/
Standalone starter repo to fork:
{:deps {com.hyperfiddle/electric {:mvn/version "v2-alpha-68-g7e22216c"}}}
- Production ready for, let's say back office apps, after 8 months of private user testing and extreme dogfooding in the Hyperfiddle sister project.
- As a maturity indicator, the only low level bug in recent memory was a hash collision triggered by scrolling a server-paginated grid over thousands of server-streamed elements.
- Stack traces aren't great; we do have async stack traces already but they need work
Current development priorities:
- developer experience improvements
- network planner improvements
- language semantics improvements
To date we have focused on correct semantics over syntax and performance. Now that we are useful in production, we are using production learnings to drive our priorities.
Community: #hyperfiddle @ clojurians.net for support; follow https://twitter.com/dustingetz for progress updates
We target full Clojure/Script compatibility (say 99%). That means you can take a pre-existing Clojure snippet and copy/paste it into an Electric function body and it will "work" and produce the correct result. Including host interop syntax, use of pre-existing macros, etc.
Gaps:
- no variable e/fn arity yet
- no recursion yet - see workaround in src-docs/user/electric/electric_recursion
- reactive multimethods
- reactive protocols
- ...
- Requires -Xss2m to compile. The default of 1m ThreadStackSize is exceeded by the Electric compiler due to large macroexpansions resulting in false StackOverflowError during analysis.
- :eval opcode - probably interop syntax, or a macro like assert that expands to interop syntax
Unbound var.
Usually means wrong peer, i.e. accessed server-only var on client
- PRs require a signed contributors agreement (like Clojure), DM dustingetz on slack.
- No typo fixes please, we are not all native English speakers and we decided it's not worth it.