The :hoplon: ClojureScript Web Framework - http://hoplon.io/
flyboarder 2018-01-23T00:12:20.000145Z

@hiskennyness it’s so you can process children in a more natural form

kennytilton 2018-01-23T00:57:13.000208Z

Actually @flyboarder, I think the reason is the unstated goal that all dom-yielding forms always accept the pattern (fn ~@[key-value-pairs] children*). Otherwise I could just specify that my-list needs all attributes in a map as the first arg perhaps with a nicety saying the map can be skipped. I really like the idea of everything being a function, but then I do not want to be hamstrung in how I write my functions.

flyboarder 2018-01-23T00:58:21.000126Z

There are many examples of just using functions, this is common for state

kennytilton 2018-01-23T01:16:06.000304Z

I think the idea is, if you want to play that game where attributes can be spliced ahead of children instead of lumped in a map, defelem is there for you. Me, I like text structure to facilitate editing.


@hiskennyness i use both defn and defelem


@chromalchemy no idea about the old for-tpl but the new one i proposed uses subvec so there's sharing for sure


wait... i'm not quite sure what you mean about "sets of DOM nodes in the cache" lol


@hiskennyness there's no downside to using defn if you haven't bought in to the attributes/children style


@thedavidmeister I guess I meant how elements are shown or hidden. Is this done with display: none? I guess the cache is the DOM, and element blink in and out of view (ie :toggle).


oh no, DOM elements are objects in their own right


they aren't being shown/hidden they are being attached/detached from the document


@hiskennyness defelem is basically sugar on hoplon.core/parse-args https://github.com/hoplon/hoplon/blob/master/src/hoplon/core.cljs#L136


@chromalchemy the elements get built originally each with a cell that has a get reference either to a position or a key in the items cell you pass in


so that cell knows how to keep itself updated with the right value based on index/key relative to items - the tpl doesn't have to do anything ongoing, just handle the original build with a sensible item cell=


the DOM elements are then just created as needed by looking up whether we already have a DOM element for that index/key


then it's returned as a cell with a list of DOM elements in it


that's the end of what for-tpl does, but then merge-kids is what takes over to actually append/detach those elements from the DOM (which could definitely be improved in the future to do more efficient diffs to minimise those DOM operations)


but AFAIK you cannot do structural sharing on a DOM element directly because not only are they totally mutable but the browser is actively mutating them all the time, often in ways that you cannot respond or detect with JS


@chromalchemy i discussed the performance of the basic for-tpl here - https://github.com/hoplon/hoplon/issues/236


@thedavidmeister Ok. That helps. I probably need to familiarize myself with basic DOM semantics (attach/detach/remove/etc).


"You can think of the way that the DOM is stored as a "cache" of sorts, but probably quite a bit smarter than most caches you may have worked with in the past. Any Javelin cells in the element definition will still be "live" even if the element is not visible." https://github.com/hoplon/hoplon/wiki/Dynamic-DOM-manipulation-aka-Template-Macros


That had me picturing mechanical caches..

kennytilton 2018-01-23T01:47:28.000043Z

@thedavimeister I understand. I think the last bit on that page is good but somewhat misdirecting.

`(defn my-list
  [& items]
  (ul (map li items)))
` is a straw man. There is no need to write it that way, so it does not justify defelem (nor is there any great need for defelem unless one does not like putting braces around maps). But this is just nitpicking. πŸ™‚


@hiskennyness that's good feedback though, we can always update the wiki πŸ™‚


@chromalchemy hah, ok maybe we can update that in the wiki too


@chromalchemy on your comment about transducers, technically it isn't the comp making the transducer, it's calling a list processing function without the list like (map my-fn) returns a transducer but (map my-fn my-list) returns a lazy sequence - the idea is that you comp the transducers first and no lists get created and then you give it your list, and all the processing is done without intermediate lists being produced

kennytilton 2018-01-23T01:55:36.000027Z

I do see a case for stanardizing the syntax. To my mind, our dom-generating functions basically deliver on web components, so maybe they shoul not go crazy on variable parameter lists. Me, I see this has totally throwing of the shackles of HTML markup. Call me a wild and crazy guy!


yeah it's pretty sweet!


actually, because parse-args is called consistently when you use an element as a fn the difference between defn and defelem is very small


(defn my-el [] (div)) then later ((my-el) :attr :foo (div) :other-attr :bar) will still work


which is important because if you want to be writing functions that take an element, add some things to it then return the element, you can't be required to know the signature of the fn used to create the element originally


So there are cases where defn is has more utility. Are there cases where defelem is preferred (beyond ergonomics)?


I've always used defelem because I have little intuition about the tradeoffs, and don't want to unknowingly break any beneficial magic 😁


@chromalchemy 90% of it is just juggling arguments and doc strings


if you find defn more convenient just use it


all elements will get the defelem syntax after they are created, we're really just talking about your elements "constructor" here


That said. As a beginner. I found defelem very welcoming because the attrs + child thing felt like a smooth continuation of my HTML experience (shackles and all!)


that's true


training wheels πŸ˜›




My first LISP: I'm a real coder now! 🎡


haha, well maybe we can reword the wiki so that it is still presented as beginner friendly but makes it obvious that there's nothing bad about defn either


brb, getting β˜•


I'm all for community conventions. Since Hoplon is so flexible, the tradeoff is that elucidating and curating the options is a great challenge in itself (Lisp Curse)


But it's pretty cool that it can works both ways. I think there's something powerful about how Hoplon can look like markup. Before I started coding, I found all the high-level stuff fascinating, but my eyes would glaze over when it came to code samples. Before you internalize syntax, it all looks like an arcanely-formatted stop sign, the symbols worse than nonsense, actively defying you to intuit the obvious and important meanings you know they have. At least until you start looking up definitions (but for many this is a HUGE leap). That's also the story for HTML. It's just that many millions have taken the leap with that particular syntax. I personally found it reassuring that Alan and Micha conceptualized hoplon as hlisp, and likened it to PHP. Now i see Hoplon as more of a generic markup simulator or VM, not merely HTML. I think it's a powerful good to continue to tend a gentle path from standard web markup + reactive -> dynamic "templating" -> bespoke (app) programming. Clojure has a problem with approachability. I see Hoplon as potentially a key gateway (It was for me at least πŸ™‚ )


@thedavidmeister Thanks for all the explanations. I'll do more on my part to grok the dom-state internals. But would you comment on this example:

(cell mylist [:a])
(for-tpl [x mylist] (elem x))
(reset! mylist [:a :b]
(reset! mylist [:c])
What is in the dom at the end? What is removed vs merely detached? If mylist, and the reset!'s had hundreds or thousands of keys, would this be putting undue stress on browser memory? (hence keyed-for-tpl...)


@chromalchemy every dom element created by the for-tpl lives in the cache indefinitely, just like memoize in core


same for keyed-for-tpl


obviously this is not ideal, but the intent is that it's an improvement on a vanilla map where DOM elements are created and kept indefinitely and also cannot be reused


for a regular for-tpl, if you have a list with thousands of elements, well you have a list with thousands of elements


it is what it is - the only way you'd have "undue" stress would be if you spiked the list up to a thousand elements then brought it back down to dozen or so


the keyed-for-tpl is too new (hasn't even been pushed into a snapshot yet) to have any fancy cache clearing logic


i listed that as something that can be followed up at some point!


in theory there's lots of different strategies that could be used to evict "stale" elements beyond a threshold - e.g. https://github.com/clojure/core.memoize/


but also keep in mind that destroying a DOM element and destroying the cells associated with the DOM element are two different things - this is where i'll probably need some help investigating when we get to it!


the short answer is - there is currently no "destroy this element and all cells associated with the element" general function to do the kind of cleanup you're thinking of that will work with the browser GC process


@chromalchemy as far as "hoplon looks like markup" well that's an old debate of lisp vs. XML - http://wiki.c2.com/?LispVsXml


under the hood (div) is mostly just sugar for document.createElement('div'); in JS


and (body (div)) is sugar for document.body.appendChild(document.createElement('div')); because body is a "singleton" in hoplon terms so it merges children into the existing element in the document rather than creating a new <body></body> element


calling the created DOM element later as a function uses the merge children logic


so ((div) (div)) is something like document.createElement('div').appendChild(document.createElement('div'));


obviously the hoplon approach is far less verbose but it's important to realise that there's no "magic" going on...


the singletons are: body, html, and head


everything else is an elem, and you can extend to new elems that we may not support yet in core by calling (def fancy-new-tag (mkelem "fancy-new-tag"))


maybe we need something like this https://kanaka.github.io/clojurescript/web/synonym.html for hoplon...


i tried to build something like that as a hoplon app a year or two ago, unfortunately it meant shuttling a huge amount of js to the client frmo the server because hoplon couldn't run entirely in browser


(i went the approach of compiling on the server because -tpl, cell= etc werent cljs compat)

flyboarder 2018-01-23T21:09:52.000317Z

@thedavidmeister @alandipert we could throw something like that up on the website, as just a side by side without eval

flyboarder 2018-01-23T21:35:49.000178Z

@thedavidmeister https://caniuse.com/#feat=childnode-remove remove is now widely supported, I would imagine we can implement our own remove method which will cleanup cells, and then call the browser remove

flyboarder 2018-01-23T21:42:30.000287Z

actually I have an idea for easily implementing this

flyboarder 2018-01-23T21:48:14.000767Z

@alandipert can we a repo with the current hoplon website in it? I’d love to convert this to Hoplon and get a side-by-side thing going on!


flyboarder there is a repo, at https://github.com/tailrecursion/hoplon.io


flyboarder i sent you an invite, have at it! thanks


the only thing i ask you preserve is the adzerk credit, in some form (doesn't need to look like that)

flyboarder 2018-01-23T21:52:49.000063Z

@alandipert cheers! will do!


@flyboarder could you push a snapshot before you get deep into that πŸ˜‰

flyboarder 2018-01-23T23:49:27.000012Z

@thedavidmeister yeah I’ll get a snapshot out tonight

flyboarder 2018-01-23T23:49:34.000020Z

It’s about 5pm here right now


awesome cheers