Fork me on GitHub
#hoplon
<
2018-01-23
>
flyboarder00:01:20

@hiskennyness itโ€™s so you can process children in a more natural form

kennytilton00:01:13

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.

flyboarder00:01:21

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

kennytilton01:01:06

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.

thedavidmeister01:01:29

@hiskennyness i use both defn and defelem

thedavidmeister01:01:38

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

thedavidmeister01:01:22

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

thedavidmeister01:01:03

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

chromalchemy01:01:31

@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).

thedavidmeister01:01:00

oh no, DOM elements are objects in their own right

thedavidmeister01:01:18

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

thedavidmeister01:01:58

@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

thedavidmeister01:01:27

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=

thedavidmeister01:01:06

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

thedavidmeister01:01:45

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

thedavidmeister01:01:02

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)

thedavidmeister01:01:50

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

thedavidmeister01:01:34

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

chromalchemy01:01:41

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

chromalchemy01:01:02

"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

chromalchemy01:01:42

That had me picturing mechanical caches..

kennytilton01:01:28

@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. ๐Ÿ™‚

thedavidmeister01:01:30

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

thedavidmeister01:01:09

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

thedavidmeister01:01:21

@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

kennytilton01:01:36

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!

thedavidmeister01:01:34

yeah it's pretty sweet!

thedavidmeister02:01:18

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

thedavidmeister02:01:15

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

thedavidmeister02:01:25

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

chromalchemy02:01:15

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

chromalchemy02:01:43

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

thedavidmeister02:01:24

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

thedavidmeister02:01:38

if you find defn more convenient just use it

thedavidmeister02:01:06

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

chromalchemy02:01:22

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!)

thedavidmeister02:01:57

training wheels ๐Ÿ˜›

chromalchemy02:01:34

My first LISP: I'm a real coder now! ๐ŸŽต

thedavidmeister02:01:41

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

thedavidmeister02:01:32

brb, getting โ˜•

chromalchemy02:01:48

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)

chromalchemy02:01:34

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 ๐Ÿ™‚ )

chromalchemy02:01:30

@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...)

thedavidmeister03:01:01

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

thedavidmeister03:01:17

same for keyed-for-tpl

thedavidmeister03:01:02

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

thedavidmeister03:01:54

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

thedavidmeister03:01:26

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

thedavidmeister03:01:14

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

thedavidmeister03:01:49

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

thedavidmeister03:01:59

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/

thedavidmeister03:01:14

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!

thedavidmeister03:01:01

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

thedavidmeister03:01:51

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

thedavidmeister03:01:27

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

thedavidmeister03:01:37

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

thedavidmeister03:01:20

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

thedavidmeister03:01:54

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

thedavidmeister03:01:47

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

thedavidmeister03:01:41

the singletons are: body, html, and head

thedavidmeister03:01:48

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"))

alandipert17:01:42

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

alandipert17:01:01

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

flyboarder21:01:52

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

flyboarder21:01:49

@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

flyboarder21:01:30

actually I have an idea for easily implementing this

flyboarder21:01:14

@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!

alandipert21:01:00

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

alandipert21:01:00

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

thedavidmeister23:01:04

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

flyboarder23:01:27

@thedavidmeister yeah Iโ€™ll get a snapshot out tonight

flyboarder23:01:34

Itโ€™s about 5pm here right now