Fork me on GitHub
#hyperfiddle
<
2024-01-10
>
joshcho13:01:34

how do i do routing in electric? are there any examples?

Dustin Getz13:01:06

easiest way is a case statement, if you search for "routing" there are several discussions

Dustin Getz13:01:09

we have a hyperfiddle.router coming for complex route compositions but it's not ready

Dustin Getz13:01:36

core.match also works

Dustin Getz13:01:04

there is a goog.history integration under the contrib namespace

❤️ 1
J14:01:41

case+`reitit`

Rob Haisfield17:01:57

@dustingetz Is Electric uniquely possible in Clojure, or would it work in any language that has macros, like Rust or Swift?

👋 1
Dustin Getz17:01:17

Strictly speaking, maybe possible. Macros not strictly needed, the compiler can be language-external (i.e. Babel). Immutability-by-default is important to the reactive dataflow semantics. Mutation tracking (for imperative langs such as JS or WASM) is a hard unsolved problem, only beginning to be tackled in the literature i've seen. So technology is not there yet. More loosely speaking, perhaps a 65% good enough solution could get adoption in mainstream, but you'd still need a modern concurrency library and there's nothing good enough in JS that I've seen (we use Missionary which is the state of art, years ahead of effect-ts)

Dustin Getz17:01:20

Clojure is the right place to do an experiment like this and be able to move reasonably quickly, and in a commercial setting with PL enthusiasts to early adopt and talk to us while we figure out the design, etc

joshcho23:01:11

this is kinda tough to describe, but i will attempt it anyhow: there is s1, s2, s3, ... and these are coming in at different times sequentially. i want to stream (in the ui) s1, s2, and s3 one character at a time, but stream in s2 only after s1, s3 only after s2, etc. so if s1 = "hello" and s2 = "there", then the user would see h, he, hel, hell, hello, then once s2 is received, hellot, helloth , etc.

joshcho23:01:57

this is what i have currently, which doesn't do any accumulation

(let [!q (atom "htht")
      q  (e/watch !q)]
  (letfn [(random-delayed-seed [fixed-delay variable-delay xs]
            (m/ap
              (m/?
               (let [x (m/?> (m/seed xs))]
                 ;; order matters here
                 (m/sleep
                  (+ fixed-delay (rand-int variable-delay))
                  x)))))]
    (when-some [x
                (->> q
                  (random-delayed-seed 50 150)
                  (m/reductions str "")
                  new)]
      (js/console.log x)
      (div (text x))))
  (div (text q))
  (dom/input
   (dom/on "keydown"
     (e/fn [e]
       (when (= "Enter" (.-key e))
         (reset! !q (.. e -target -value)))))))

Vincent23:01:00

josh this is really interesting what's your desired outcome? trickle-write of any streamed input in any asynchronous order received?

joshcho23:01:20

(for llms)

Vincent23:01:25

is there a good reason the llm would give you a strange output sequence ? or is that more of a design consideration.

joshcho23:01:33

lmao it's a result of a hack i am doing for our project

joshcho23:01:35

long story

joshcho23:01:41

it's a very strange use case

joshcho23:01:00

but very critical for our use case

joshcho23:01:58

also a hunch that this is much more easily solved using differential electric

Vincent00:01:50

how do we know the appropriate sequence of stuff? is everything tagged with a sequence number on the way in?

joshcho00:01:23

yeah s1 s2 s3 are sequential inputs

joshcho00:01:31

reset! of an atom

Vincent00:01:42

Oh okay, they are coming in in the correct sequence already, gotcha. I reckon putting it on a channel is the right answer

joshcho00:01:11

i haven't dealt with channels too much, how would i do that?

Vincent00:01:41

basically using put and take to load and unload the chan

joshcho00:01:50

how does that translate to electric

joshcho00:01:13

\ are there examples?

Vincent00:01:54

put and take utilize go blocks, i wonder abt electric examples

joshcho00:01:56

i am assuming just reset! of atom is not enough because it'll completely forget about previous values

Vincent00:01:08

yeah some sort of state tracking

Vincent00:01:13

seems necessary

Vincent00:01:59

told chatGPT4 to generate an example with put and take, does it look right ?

(ns your-namespace
  (:require [clojure.core.async :refer [chan put! take! go]]
            [cljs-http.client :as http]))

(defn get-llm-response [request]
  ;; Function to get a response from the LLM (simulated here as an HTTP request)
  ;; In a real scenario, you would replace this with an actual HTTP request to the LLM API
  (http/get ""
            {:params {:query request}}))

(defn process-llm-response [response]
  ;; Function to process the response from the LLM
  (println "Received response from LLM:" response))

(defn send-request-to-llm [request channel]
  ;; Function to send a request to the LLM and put the response into the channel
  (go (let [response (<! (get-llm-response request))]
        (put! channel response))))

(defn main []
  ;; Main function to demonstrate put! and take!
  (let [channel (chan)] ; Create a new channel
    ;; Send a request to the LLM
    (send-request-to-llm "Hello LLM" channel)

    ;; Take the response from the channel and process it
    (go (let [response (<! channel)]
          (process-llm-response response)))))

(main)

joshcho00:01:13

can't quite figure it out...

(let [!q (atom "htonuehto")
      q  (e/watch !q)
      ch (a/chan)]
  (div (text ch))
  (letfn [(random-delayed-seed [fixed-delay variable-delay xs]
            (m/ap
              (m/?
               (let [x (m/?> (m/seed (m/?> xs)))]
                 (m/sleep
                  (+ 20 (rand-int 200))
                  x)))))]
    (when-some [x
                (->> (channel-flow ch)
                  (random-delayed-seed 50 150)
                  (m/reductions str "")
                  new)]
      (js/console.log x)
      (div (text x))))
  (div (text q))
  (dom/input
   (dom/on "keydown"
     (e/fn [e]
       (when (= "Enter" (.-key e))
         (a/put! ch (.. e -target -value)))))))

Vincent00:01:43

i think you're on the right track

Vincent00:01:47

not sure about new)

joshcho00:01:27

i think channel-flow doesn't work in cljs

joshcho00:01:04

this code

(defn forever [task]
  (m/ap (m/? (m/?> (m/seed (repeat task))))))

(defn- take! [chan]
  (m/via
      m/blk
    (let [r (a/<!! chan)]
      (if (nil? r)
        (throw (ex-info "take cancelled, channel closed" {:cancelled? true}))
        r))))

(defn channel-flow [ch-recv]
  (forever (take! ch-recv)))

joshcho00:01:52

new converts it to electric value

joshcho00:01:54

whatever it is, i keep getting #object[Error Error: Unsupported operation.] core.cljs:200:23

joshcho00:01:19

omg i did it

joshcho00:01:30

i haven't used my brain this much since my operating systems class lmaooooo

👏 1
joshcho00:01:42

all i was missing was e/fn [] that turned it into a flow

Vincent00:01:34

Oh very cool)

xificurC10:01:14

(m/seed (m/?> xs)) is just xs

joshcho00:01:30

@U09FL65DK i am noticing difference in behavior between those two