This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-08-23
Channels
- # babashka (4)
- # beginners (46)
- # biff (64)
- # calva (34)
- # cider (29)
- # cljdoc (12)
- # cljs-dev (16)
- # clojure (42)
- # clojure-australia (2)
- # clojure-china (1)
- # clojure-europe (35)
- # clojure-filipino (1)
- # clojure-hk (1)
- # clojure-indonesia (1)
- # clojure-japan (1)
- # clojure-korea (1)
- # clojure-my (1)
- # clojure-nl (1)
- # clojure-norway (6)
- # clojure-sg (1)
- # clojure-taiwan (1)
- # clojure-uk (4)
- # clojurescript (3)
- # core-typed (3)
- # cursive (5)
- # datalevin (3)
- # datomic (23)
- # hyperfiddle (92)
- # joyride (8)
- # juxt (3)
- # malli (1)
- # nbb (44)
- # pathom (10)
- # portal (3)
- # rdf (1)
- # reitit (10)
- # shadow-cljs (60)
- # sql (12)
here is the rama social network demo https://github.com/redplanetlabs/rama-examples/blob/master/src/main/java/rama/examples/ramaspace/RamaSpaceModule.java
twitter thread with video: https://x.com/nathanmarz/status/1694046536861368594?s=61&t=lI9nNO9bkbL1YKFrdIw_Tg
Hopefully they’ll relent and give access to the Clojure API, because building a bespoke Clojure API around a Java API around a Clojure API is silly.
I'm new to following this stuff. Can anyone tell me how Rama relates to electric? Is it another take on the idea of combining front and backend, or somehow complimentary?
It looks like it's a paradigm similar to differential dataflow?
in theory at least
Am i right that its a framework for streaming incremental updates to data similar to differential dataflow then?
i don’t know if rama “subindexing” is actually differential dataflow or not (i doubt it and i think that’s probably a mistake). I can say that the next major version of Electric is indeed differential dataflow
It is no coincident that when you see the Paths in the demo that they look like Specter, because they are both supported by the team. I would think this could mean that a RAMA Clojure implementation could be on the cards.
Rama is written in Clojure, so it already exists. The “native” API isn’t exposed though.
I was just looking at the video where it is all Java, seeing the groovy examples, immediately makes you want to usea Clojure variant as it would be much simpler.
Like, they call $$…
“vars”. Probably because they are vars in the Clojure API language.
The code that is written in their examples, is really hard to understand as you are using one language to write another, I would image that macros, could be created to make it simpler to create the code more directly. Having worked in solutions where you are generating code from code, you find the debugging the emitted code is complex as you don't have the support of IDE etc.
@dustingetz Do you imagine subindexing being DD or not would surface in the API, or more of a mistake regarding the efficiency of the internals?
The examples certainly do some contortions due to (I’m guessing) the limitations of Java. Variables being strings beginning with *
/`$$`, and if you want a literal string with those prefixes you must wrap them in Constant
comes to mind.
I stumbled on a line where they mentioned the importance of ordering, which is a prerequisite for timely dataflow
the differential dataflow end user programming model, as I understand it and as we are implementing it in Electric, is FRP like and DAG based (in Electric you build your app out of lambdas and closures). As opposed to the Rama end user programming model where you program with structs and you have to plan out the data structures. My initial impression is Rama feels like intrusive data structures in C as opposed to generic data structures in Java. Faster but lower level, used in operating systems and games. https://stackoverflow.com/questions/5004162/what-does-it-mean-for-a-data-structure-to-be-intrusive
DD exists at a different abstraction level, too. It's about sets (signals? maybe), not about a specific value or a specific time
What is recommended to NOT update a value if the input is under some condition? Do we have to save the previous value in a state?
When syncing text data from server to client, we need to keep track of whether the server’s data is outdated by comparing versions. If the incoming data is outdated, then the client state won’t be updated. To prevent a function from being updated in electric context , we need to calculate the previous value. Is there a recommended way to achieve this ?
Say we have:
(e/defn my-func [a b] (+ a b) )
In state1 , a=1, b=1, result = 2
In state2 , a=2, b=1, result = 3
Can we block state2's update under some condition, and let the result remain 2? Such as :
(e/defn my-func [a b] (if block-condition previous-value (+ a b) )
> When syncing text data from server to client, we need to keep track of whether the server’s data is outdated by comparing versions How does this situation arise? Electric doesn't produce out-of-order data. Is your server sending old data to the UI?
to also answer the broader question (how to act on previous values), it is typical to introduce state here (an atom)
(let [!v (atom 0)]
(when condition (reset! !v (+ a b)))
(e/watch !v))
> How does this situation arise? Electric doesn’t produce out-of-order data. Is your server sending old data to the UI?
The outdatedness is defined in our application domain, not in system level. We try to implement an offline-first model where there are n+1 copies of data. And we annotate different copies by version
and event-time
to resolve conflict.
> to also answer the broader question (how to act on previous values), it is typical to introduce state here (an atom) Ah I see. Thanks for showing this method.
note that state and recursion are deeply related and/or the same thing. Consider a Java for loop with mutable accumulator vs reduce
So, I hope next year we can eliminate the need for an atom here, but it is not a priority, and i don't find the atom to be problematic for this reason
I am trying to run the electric-datomic-browser, but I can't get shadow-cljs to compile the front
clj -A:dev -X user/main
Starting Electric compiler and server...
shadow-cljs - server version: 2.25.3 running at
shadow-cljs - nREPL server started on port 9001
[:dev] Configuring build.
[:dev] Compiling ...
[:dev] Build failure:
------ ERROR -------------------------------------------------------------------
File: jar:file:/Users/danbunea/.m2/repository/thheller/shadow-cljs/2.25.3/shadow-cljs-2.25.3.jar!/shadow/cljs/devtools/client/hud.cljs:1:1
--------------------------------------------------------------------------------
1 | (ns shadow.cljs.devtools.client.hud
-------^------------------------------------------------------------------------
Invalid :refer, var goog.string/format does not exist
--------------------------------------------------------------------------------
2 | (:require
3 | [shadow.dom :as dom]
4 | [shadow.animate :as anim]
5 | [shadow.cljs.devtools.client.env :as env]
--------------------------------------------------------------------------------
WARN app.server: Port 8080 was not available, retrying with 8081
:point_right: App server available at
I did see that in deps.edn, we have an older version:
:dev
{:extra-paths ["src-dev"] ; fix uberjar java.lang.NoClassDefFoundError: clojure/tools/logging/impl/LoggerFactory
:extra-deps
{...
thheller/shadow-cljs {:mvn/version "2.22.10"}}
We saw this. We suspect a corrupted .shadow-cljs
cache.
We are not yet clear on the root cause.
You can try to
• stop the shadow process (not just the build, stop the JVM)
• rm -rf .shadow-cljs
• restart the shadow build
It’s worth noting we only saw this behavior with watch
, never with compile
or release
Hey, thanks for answering: • ✅ remove the cache .shadow-cljs • ✅ stopped everything • 🔴 clj -A:dev -X user/main
I see WARN app.server: Port 8080 was not available, retrying with 8081
, could there be a second shadow process running ?
I can reproduce the issue with a clean repo clone. Looking into it
thank you. My use case of using electric is very much dependent on datomic so I thought I could start from this example
I do not recommend starting here, we haven't touched it in ages
I did host it this week, and am working on unifying how builds work in our dozen or so repos this week
Though now that Datomic is free, I'll make the starter app use Datomic
If you want the contributor badge lmk and you can send a PR if you like
We have a minimal repro for the shadow build issue. We’ll address it. Thank you for the report 👍
I pushed a new commit which fixed this on my end, can someone from this thread double check on their end? The git coords to use are
io.github.hyperfiddle/electric {:git/sha "22937f751db7258ac0be0337833c036dbc4879f2"}
How feasible is it to use Malli https://github.com/metosin/malli/blob/master/docs/function-schemas.md on Electric functions?
since an e/fn
has the same structure as a clojure fn it should be doable. I'm not sure how malli checks functions and how much effort it would be though. Are you familiar with malli's internals?
I am not unfortunately; I'm just now trying to integrate it. I may resort to just using instrumented validation inside the functions for arguments, as many of the other feature of function schemas likely aren't applicable to Electric. (Generative testing, generation functions, etc.)
Most of the function schema/instrumentation functionality likely won't work, as Malli expects to work with plain functions which it can wrap in another fn
. (Which then wouldn't have the Electric metadata)
Are Electric functions expected to support https://clojure.org/reference/special_forms#_fn_name_param_condition_map_expr?
Or varargs for that matter
varargs are actually just released yesterday to clojars i haven’t announced it yet, see change log
Oh, nice 🙂
condition. maps- no, i’ll add a ticket. Our compat maturity is currently adhoc we implement what we need. When we come up for air (after differential electric, incremental builds, optimistic ui) we will perhaps make a compatibility matrix and ratchet it down
👌 Makes sense; thank you 🙂
This feels like a reasonable usage for Malli validation of function inputs
(e/defn Button [& args]
(let [schema [:catn [:x string?]]
{:keys [x]} (m/coerce schema args)]
(dom/button
(dom/text (str "Click me! " x)))))
It doesn't provide the same niceties as Malli's builtin function schemas though. (Output validation, clj-kondo integration, generators, etc.)Are Electric functions expected to support https://clojure.org/reference/special_forms#_fn_name_param_condition_map_expr?
@dustingetz, seems like something may have changed with
(User error)
v2-422
; I'm getting an error where dom/new-node
is being called with a parent
of a blank string despite having a wrapping binding to a real dom node. (See https://github.com/garrett-hopper/ElectricStorybookExamples/blob/main/src/utils/storybook.clj#L17)
Failed to execute 'appendChild' on 'Node': This node type does not support this method.
in ( hyperfiddle.electric-dom2/new-node #object[Text [object Text]] button ) in app/books.cljc
in case default branch
in (case 1 ...)
in reactive (defn Button nil ...) in app/books.cljc line 16
in ( hyperfiddle.electric-dom2/-googDomSetTextContentNoWarn #object[Text [object Text]] <exception> ) in app/books.cljc
in case default branch
in (case 0 ...)
in reactive (fn nil ...)
in (try ...)
I can come up with a minimal reproduction if it's not immediately clear what might've changed about this behavior.
we will take a look, might not be until tomorrow sorry 😕
from a quick glance - that's a lazy seq, does making it strict fix the issue?
i dont see how that could actually be something that changed
The bindings? I don't think they have anything to do with it.
Ok, I'll verify that it's the version change that caused it.
Oh, i believe that it could be a breaking change in v2-422
Hmm, this does appear to work in isolation, so it may not be the initial problem I thought it was.
(def start-reactor
(let [node (js/document.createElement "div")]
(.appendChild js/document.body node)
(e/boot
(binding [dom/node node]
(core/App.)))))
I'm not sure why the dom/node binding isn't working as expected in the full example though. :thinking_face:
is there react/reagent interop?
No, nothing like that
Adding a breakpoint here, is it expected that a child text type would be called first before the parent button?
parent
is undefined for both calls 😕
parent should not be undefined
i dont seem to be looking at the same code as you because core/App is not defined
https://github.com/garrett-hopper/ElectricStorybookExamples/blob/main/src/app/core.cljc
I'm sorry. I think I was just being dumb and had a component being mounted inside dom/text
. :man-facepalming:
where
Not in the macro I sent you, but where I'm using it, at some point I'd tried to test something and apparently ended up with
(defbook Button
(dom/text (Button. 5)))
So when Button
when to mount, it obviously didn't have a parent since it's in a text node
and you're saying this worked in v2-349?
No, I don't believe so. I think it was around the same time that I had upgraded that I was also doing the :pre/:post testing and instead of Button
, I had an electric function that actually returned a string rather than trying to mount child components.
Entirely user error; I apologize 😕