This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-01-21
Channels
- # arachne (21)
- # beginners (22)
- # boot (58)
- # cider (27)
- # cljs-dev (67)
- # cljsjs (10)
- # cljsrn (13)
- # clojure (91)
- # clojure-greece (6)
- # clojure-russia (1)
- # clojure-uk (6)
- # clojurescript (6)
- # core-async (3)
- # cursive (6)
- # datomic (3)
- # events (2)
- # hoplon (152)
- # off-topic (44)
- # om (8)
- # om-next (7)
- # onyx (16)
- # protorepl (10)
- # re-frame (10)
- # reagent (22)
- # untangled (5)
@flyboarder @jumblerg yeah the enter/update/exit stuff kind of got in the way of javelin, so i stopped using it, but the things like line generator functions are great
@qqq this isn't really answering your q, but fyi you can use regular ajax/ws pretty easily by just putting the responses in a cell
@thedavidmeister: I don't understand your response. with gae, I will be using clj on both client and server side
so if castra is already providing rpc to my gae/clj/server side, what would I be using ajax/ws for ?
oh i mean, i don't use castra
if you're just getting started, there's other options 🙂
castra is probably the main reason I'm testing a switch from re-frame to hoplon/javelin/castra
oh, well in that case, carry on
that you can do
now my qeustion is -- does someone have a nice banding integrating the clj/server side of castra -- with the google app engine java bindings
i have used castra for some basic stuff
i don't personally, but that doesn't mean it's not out there
do you konw what the hl part of *.cljs.hl provides over plain cljs? I know that boot does some .cljs.hl -> .cljs compilation, but I have yet to figure out why we can't just use plain cljs - i.e. waht addiitonal expressivitiy is .cljs.hl providing
i just use plain .cljs
my index file looks like this
(page "index.html"
(:require [bootstrap.core]
[pages.dom]))
(pages.dom/current-page)
there's a blog post floating around that goes into way more detail about what .hl is actually doing
maybe @alandipert can point you in the right direction
one mpre question: in https://github.com/hoplon/hoplon/wiki/Get-Started
(ol
(loop-tpl :bindings [n history]
(li (cell= (str "n was " n)))))
section "Display Sequence in DOM"
is it recreating a new list and re-rendering all itmes - or is it smart enough to just add one item?
i.e. is it O(n) or O(1) dom events when I conj to the historynot recreating the list
that's why it exists
actually, slightly more complicated, but not much
when you add something it gets added
when you modify a cell it propagates values into the dom you've built
if you remove something, it gets detached from the dom but is still there in memory, ready to be added back in, like a cache
actually, you should know that hoplon doesn't re-render all items
at all
that's what certain other frameworks are attempting to do/simulate
it's just creating dom elements and binding events so that their attributes match cell values
that should definitely be in the wiki if it isn't already >.<
a very common q
here's the thing the history function is defined as:
(add-watch clicks :history #(swap! history conj %))
now, hoplon has no idea that we're doing append only -- on an update, hoplon only sees:
(1) old history list
(2) new history list
so then, does hoplon take these two data structures, diffs them (i.e. gets a list of add, del, update) -- then it does the DOM magic ?aaah, so there's not really any "dom magic"
that's the thing
think of it more like, you have cells, and when their value "changes" (just checks equality of before/after)
then hoplon will make the dom elements have the new value for whatever
well, if you're just adding 1
pretty sure that the loop tpl is making cell= under the hood
so there will be equality checks and nothing will happen in the dom
the two places cells interact with the dom is for providing attribute values and for populating the children of an element
could you point me at the code for this? I would love to read this and see what's happening uner the hood
sure, 1 sec
the easiest way to see what's going on is to look at the html inspector, at a loop-tpl-generated section of nodes
one more thing; just so we're on the same page in the example we're talkng about, the "history" field is ONE CELL, with a HUGE LIST (say 2000 items) right? i.e. is't NOT 2000 individual cells
basically, depending on how many children need to be appended... if the number of children doesn't exceed the previous maximum number of children that have been added, nodes that were already created are inserted back into the dom
that's correct re: history. it's a cell that contains a vector of 1k things
this might be useful btw. it is a cocneptual overview i have been working on the past couple weeks on and off
it's maybe half done : https://github.com/hoplon/hoplon/wiki/Hoplon-Overview
suppose my old list was [1 2 3 4 5], and my new list is [0 1 2 3 4 5] // in this case, does hoplon llook at this and go "hmm, we can be smart and insert a 0 at the front", or does it try to do update 1 -> 0, update 2 -> 1, update 3-> 2, update 4 - > 3, update 5 -> 4, insert 5 ?
so for the first list, 5 cells have been created, and each one is pointed at an index in that vector in the cell
when the value changes they all look at the index they were assigned, and if it's different, they update the dom node they own
yeah, so it's two different qs
in the first case, just the new thing would be added
the idea here vs. the 'dirty check' react approach is that updates are "precise"
assume: history = list of N elements then (1) insert 1 item at end = O(1) dom calls (2) insert 1 item at front = O(N) dom calls ^^ are both assumptionsa bove corect?
in the second, you'll update what's different
there are 3 diff kinds of dom calls involved - creating a node, inserting a node, and updating an attribute or text content
i think (2) is correct in that there are O(n) updates
but no new nodes are created
(1) is correct too, it would create a new node
oh right
and not update everything else
`
==========
history = [1, 2, 3, .... N]
question 1:
if we go from [1, 2, 3, .., N] to [1, 2, 3, ..., N+1], then we have:
O(1) create new cells, O(1) insert new nodes, no updates
question 2:
if we go from [1, 2 ,3, ..., N] to [0, 1, 2, 3, .., N}, then we have:
O(n) updates // becasue everything has to change
O(1) create new, O(1) insert // for the final one
because the ith item gets wrapped in it's own cell= with an nth pointing to the original list
inside loop-tpl
yeah i think that's right
i concur
an excellent summary ⭐
the code is not big tho, def. worth the read if this interests you
loop-tpl is thin veneer over a few functions
it's about 30 LOC
it's interesting to think about alternative strategies, to efficiently handle prepend
a reverse-allocating loop-tpl
the main thing you guys pull off vs react is that (1) you diff on the data, before ocnverting to DOM; then you converr hte "deltaon data" to DOM ops; whereas (2) in react, they take the data, render to vdom, then diff on the vdom
i think
ith-item #(cell= (safe-nth items-seq %))
e (or (shift! on-deck) (tpl (ith-item i)))
yeah that's the key thing i think
answers your q's
in loop-tpl*
but totally, all the diffing happens in the data
there's no diffing the dom ever
or a vdom
or whatever
so, like anything else, you can diff that data efficiently or inefficiently... it's just an approach 🙂
i think you've identified an interesting case there tho, where a vdom with the right heuristic could do a better job by prepending
but the vdom heuristics are also where they get extremely complex
1) I think the same heuristics can be applied to the data rather than the vdom 2) I thikn this is O(n) time to do the 'diff' anyway
if i had to do it i would probably reverse the list elements in css lol
well you could do something in hoplon too, but you'd need to do something like an "identity function"
https://www.clear.rice.edu/comp130/12spring/editdist/ <-- this is what you want I think
oooh, looks super neat
this is ezxtremlely well studied due to bioinformatics people trying to "align" gene sequences
yeah i've seen it for strings in spell checkers 😛
doesn't seem like a clear win, since calculating the edit must be at worst o(n)
sorry, i should rephrase as: (1) it does not save any running time on "diffing data" (2) but it might make thigns faster by reducing # of dom updates
mmm, it's a conceptual shift
moving from "this cell = this dom thingy"
so one extra thing is
even when nodes are removed from the dom
they are still updated. ie the cell responsible for updating them is still in action, and attached to cell graph
to "cells happen and then something maps them to a bunch of dom edits"
ok i gotta go for now
super interesting discussion tho! thanks guys, goodnight
haha go for it
i would enjoy seeing it
later
likewise, ciao
@alandipert @thedavidmeister : as promised, here is the code if the input is of of length $N$, and se search for edit distances up to $M$, this takes O(N + M^2) time the space usage is O(N * M), but can be trivaillyr educed to O(M^2); in practice, we expect N to be large, like > 100 and $M$ to be tiny, like < 10
(ns scratch)
(defn extend-path [[n lst] e]
;; (println "extend-path: ")
;; (println "n: " n )
;; (println "lst: " lst )
;; (println "e: " e )
(if (nil? e)
[n lst]
[(+ n 1) (cons e lst)]))
(defn best-of [paths]
(if (empty? paths)
nil
(first (sort-by first paths))))
(def empty-path
[0 '()])
(defn select-opts [[cur-best i1 i2 ] & lst]
;; (println "\n\n\n\n\n")
;; (println "searching for best for: " [i1 i2])
;; (println " using paths: " lst)
(let [ans (best-of
(filter #(not (nil? %))
(for [[[d1 d2] node] lst]
(let [old-path (get cur-best [(+ i1 d1) (+ i2 d2)])]
;; (println " old-path: " [(+ i1 d1) (+ i2 d2) old-path])
(if old-path
(extend-path old-path node))))))]
ans))
(defn update-one-step [cur-best i1 i2 x1 x2]
(let [best-opt (cond
(and (nil? x1) (nil? x2)) empty-path
(nil? x1) (select-opts [cur-best i1 i2]
[[0 1] [:insert-before i1 x2]])
(nil? x2) (select-opts [cur-best i1 i2]
[[1 0] [:delete i1]])
(= x1 x2) (select-opts [cur-best i1 i2]
[[1 1] nil])
:else (select-opts [cur-best i1 i2]
[[1 1] [:update-value i1 x2]]
[[0 1] [:insert-before i1 x2]]
[[1 0] [:delete i1]]))]
(if best-opt
(assoc! cur-best [i1 i2] best-opt)
cur-best)))
(defn align [v1 v2 m]
(doseq [x v1] (assert (not (nil? x))))
(doseq [x v2] (assert (not (nil? x))))
(let [len1 (count v1)
len2 (count v2)]
(if (> (Math/abs (- len1 len2)) m)
"unable to align within m steps"
(persistent! (reduce (fn [cur-best [i1 i2]]
(update-one-step cur-best i1 i2 (nth v1 i1 nil) (nth v2 i2 nil)))
(transient {})
(for [i1 (reverse (range (+ len1 1)))
i2 (reverse (range (- i1 m) (+ i1 m 1) 1))]
[i1 i2]))))))
;; if edit distance < m, we are guaranteedd to find opt
;; sometimes when opt edit distance > m, we can still find it
;; takes O(n + m^2) time, where
;; n = number of elements
;; m = max edit distance to consider
(println (get
(align [0 1 2 3 4 5 6 7 8 9 10] [:batman 0 1 3 4 5 6 :foo 9 :bar] 4)
[0 0]))
;; [5 ([:insert-before 0 :batman] [:delete 2] [:update-value 7 :foo] [:delete 8] [:update-value 10 :bar])]
(println (get
(align [0 1 2 3] [:batman 0 1 :turing 3] 2)
[0 0]))
;; [2 ([:insert-before 0 :batman] [:update-value 2 :turing])]
Nice discussion! Clarified my understanding of the hoplon model for sure 😄
I was searching for some nice ways to do routing in hoplon application, ended up finding Secretary project, but it seems to be abandoned, any other alternatives ?
some listed here: https://github.com/juxt/bidi
with Hoplon people often just use a Cell though, e.g https://github.com/hoplon/ui/blob/master/lib/src/hoplon/ui.cljs#L496 look at how the route
cell works
cell doesn’t seem to be enough for the particular case, will check the juxt/bidi link though, thanks! 🙂
@zilvinasu: check hoplon/brew there is already a bidi adapter
You will have to build locally tho