Fork me on GitHub
#hoplon
<
2016-04-08
>
leontalbot13:04:08

Hello! @alandipert @micha Should Hoplon readme mention Transit instead of Cljson?

jumblerg13:04:30

@leontalbot: if you’re updating the readme, i imagine it should probably be struck from it entirely, since transit is merely a dependency of castra (and cljson has been deprecated in favor of transit).

jumblerg16:04:20

@micha: am thinking about the do! multimethod in the context of trying to build up larger components. while do! specifies how to “write” to each component, it does not specify how to “read”. maybe it should take two functions instead for both get and set to facilitate the construction of a general maplike api over the concrete oop element interface. should be able to do not only (div :css/width “100px”), but also (div :css/width) / (:css/width div).

jumblerg16:04:10

as such, a lens might come in handy here. the use of cells on the read side can also help ameliorate many of the timing issues associated with getting the computed properties of elements when they are rendered in the dom.

jumblerg16:04:38

maybe also consider extending the js/Element type with IAssociative, ILookup, IMap, et. al

jumblerg17:04:48

i’m thinking of each Element as a model with two interfaces: the first used by the browser for rendering, the second defined and used by hoplon. lenses can be used to prescribe the two way bindings between them (with the added benefit of replacing the remaining jQuery machinery used in the do! multimethods).

jumblerg17:04:57

the user should be able to interact with each element as a map, one that contains properties on the left side and corresponding values as cells on the right. this would make possible things like computing the width of an element to fill a remaining space once it is rendered. eg (div :css/width (cell= (- (:css/width window) (+ (:css/width elem-a) (:css/width elem-b)))) …).

jumblerg18:04:35

this allows you to just declare what you want the element to do, whenever it is the browser happens to compute the sizes of the dependent elements (and, assuming the window object is given a similar treatment), readjust the size of the element when the window is resized.

micha18:04:40

i don't kno wif that is appropriate for hoplon to do though

micha18:04:53

that seems like something at a way higher level

jumblerg18:04:22

perhaps. i’ve been thinking of hoplon as a sort of shim between other stuff (like my ui lib) and the browser, where every abstraction in hoplon has a 1:1 correlation with the corresponding thing in the browser.

jumblerg18:04:37

layers on top abstract that away

micha18:04:52

that would be a lot of cpu cycles to create all those cells, perform reflection on every single element to get its properties

micha18:04:59

each element has hundreds of properties

jumblerg18:04:13

yeah, i was wondering what the perf implications of that would be...

micha18:04:25

and a lot of those properties are weird native code things that aren't even valid javascript things

jumblerg18:04:08

it seems like the right abstraction to me, but is it practically implementable? i dunno. hoplon could only expose a subset of those properties, the ones ppl would need.

micha18:04:46

i think maps is the wrong approach for another reason also

micha18:04:54

because maps in clojure are immutable

micha18:04:06

the interface doesn't really work with stateful objects

micha18:04:15

the interop interface makes a lot more sense to me there

micha18:04:51

think about how you would handle the case where an object gets a new property after you've created the map interface

jumblerg18:04:04

well, the map would be immutable, but the cells as the values, as reference types, would not be in this approach.

jumblerg18:04:47

(swap! elem assoc :css/width “100px”)

jumblerg18:04:18

becomes simpler if in a formula cell, obviously

micha18:04:50

elements would be cells?

micha18:04:28

i guess the real issue is worse

micha18:04:40

there is no way to be notified when a property changes

micha18:04:12

one day it might be supported by browsers, there are things in the works i think

jumblerg18:04:25

maybe there aren’t enough callbacks, not sure

micha18:04:08

like how do you make a cell that contains the value of elem.style.color for example?

micha18:04:30

if i do elem.style.color = "red" in javascript

micha18:04:45

how would a cell know that the thing changed?

jumblerg18:04:53

well, there would be two possibilities in this case, it would be change by (1) one, the browser, or (2) the application. in the case of the application, you’d know because it would be set via the same interface. insofar as the browser is concerned (in which case it would only be changed by maybe some third party style sheet) it might not be possible.

micha18:04:14

for things like width it's an issue

jumblerg18:04:14

checking the cssom….

micha18:04:26

because the width is really determined by the browser during layout

jumblerg18:04:34

so for width, we know when the browser is resized.

micha18:04:35

but here are lots of things like that

micha18:04:07

properties that are not really javascript things

micha18:04:18

properties that are actually pointers into the native code

micha18:04:30

so they change magically

jumblerg18:04:04

i’m sure there are holes, i just wonder how many there would be in practice if it was worked through. it might not be possible.

micha18:04:20

that's true

micha18:04:30

you don't need to support everything all at once

micha18:04:44

some support would be better than nothing

jumblerg18:04:21

my general observation is that the read/get/query side of things has been neglected by hoplon so far.

jumblerg18:04:44

if it were possible to make improvements here, it would make composing larger components on top much easier.

jumblerg18:04:30

i’m really talking about two things, i guess: 1. creating a general interface and 2. making that interface reactive.

jumblerg18:04:49

i guess the drawbacks in my proposal are, in the case of 1, suggesting a mutable Element has semantics of an immutable object by implementing a map like interface might not be ideal.

jumblerg18:04:11

and in the case of 2, the hooks might not exist to detect changes to a large number of the properties

jumblerg18:04:54

(there’s always polling, which if just a hook into the event loop, might not make that big a difference, i dunno. everything is possible if you ignore performance entirely. :)

dm318:04:02

not sure how well that works

jumblerg18:04:47

interesting. did they forget ie 10 on that chart or am i missing something?

dm318:04:13

guess it's not a browser simple_smile

jumblerg18:04:28

most of these things (like the cssom stuff) tend to be ie 10 up and everything else.

jumblerg18:04:05

@dm3: lol. my error message for incompatible versions of ie typically read, “you’re using internet explorer x. now please download a web browser. <links here>."

jumblerg18:04:40

another thought: if we have 2 (via mutation observer), we’ll also be able to solve 1.

dm318:04:38

you can seemingly do Object.observe on DOM elements

jumblerg18:04:45

problem 3, however, is general performance.

micha18:04:57

i don't think it's a real thing except in chrome

jumblerg18:04:27

i’m educating myself on mutation observer now...

dm318:04:31

yeah, object.observe seems to be a fairy tale: http://caniuse.com/#feat=object-observe

dm318:04:14

so MutationObserver is an interesting thing to look into

jumblerg18:04:26

"Probably the biggest audience for this new api are the people that write JS frameworks, mainly to solve problems and create interactions they could not have done previously, or at least not with reasonable performance. Another use case would be situations where you are using frameworks that manipulate the DOM and need to react to these modifications efficiently ( and without setTimeout hacks! )."

jumblerg18:04:12

setTimeout = polling

alandipert18:04:40

iirc mutation observers are dead

dm318:04:53

mutation events maybe?

dm318:04:11

Mutation Observer - LS
Method for observing and reacting to changes to the DOM. Replaces MutationEvents, which is deprecated.

jumblerg18:04:17

observers replaced events

jumblerg18:04:10

right now every element is backed by a couple atoms anyway, if we shove a bit more state in that atom by way of a mutation observer, then use lenses to create two way bindings to an associative api...

alandipert18:04:37

i misremembered, that was Object.observe which is a JS thing, not a dom thing

micha18:04:23

@jumblerg: they contain atoms currently, not cells

micha18:04:57

and the atoms are only modified when you add or remove elements from the dom, which is rare in hoplon

jumblerg18:04:03

atoms = memory use, cells = cpu

micha18:04:19

cells are just more expensive to create

micha18:04:33

and to update

jumblerg18:04:36

yeah, you’re definitely the expert there, i have no idea

micha18:04:45

cells do a lot more stuff

micha18:04:07

in the case of the atoms in the elements they're just holding the list of children

micha18:04:25

probably the same overhead as a js array really

jumblerg18:04:42

could have a mutation observer set a cell for each property maybe?

micha18:04:05

id the mutation observer a real thing that actually exists though?

micha18:04:09

i mean "is"

jumblerg18:04:17

caniuse says so

jumblerg18:04:30

and mozilla

dm318:04:40

if every element created in Hoplon registered a watch in the observer and a cell for an attribute would only be created when requested e.g. (:css/width elem)

micha18:04:41

i mean in ie

dm318:04:49

IE 11+ it says 😕

jumblerg18:04:56

yeah, but they forgot 10

jumblerg18:04:58

i’ll find out

jumblerg18:04:09

i bet it is in 10 as well

jumblerg18:04:38

all the cssom stuff (like the apis i used to implement media queries) started in 10.

dm318:04:04

there's a polyfill, which is super slow I'd guess

dm318:04:06

that's an interesting avenue to explore though @jumblerg

dm318:04:24

also MutationObserver can only observe things that are visible in the HTML (.innerHtml of the node)

dm318:04:30

so no computed/IDL attributes

dm319:04:11

(not sure if I'm using "IDL" properly)

jumblerg19:04:37

yeah, wonder if there could be something there besides a dead end..

jumblerg19:04:12

@micha: can we defer cell creation, make it lazy? thereby create cells only for properties we need to bind to?

dm319:04:14

if element implemented IGet (or whatever), then on (:attr/width elem) it would create a cell/atom and put it into a collection observed by the MutationObserver. Then the cell would be removed from the observed collection once the element is removed from the DOM. Would this work?

dm319:04:46

(I mean - obvious problems)

jumblerg19:04:12

something like that, yeah

dm319:04:27

there was a discussion on having a map interface on elements and I recall Micha being opposed to that

dm319:04:47

I understand the inherent mutability of DOM was one reason

micha19:04:00

i think it was generally not seen as something we needed at the end

dm319:04:18

must say having the ability to get a cell with the attribute's value is very interesting

micha19:04:26

if there is an associated event it's easy

micha19:04:42

(with-let [c (cell (.-foo elem))] (.on elem "fooChange" #(reset! c (.-foo elem))))

dm319:04:27

what's fooChange?

micha19:04:32

the event that fires when the foo property changes

micha19:04:44

assuming such a thing exists

jumblerg19:04:37

...but our elem model of the dom, if we do this, is immutable

jumblerg19:04:44

so then the map makes sense

jumblerg19:04:14

insofar as the need is concerned, i have a number of cases.

jumblerg19:04:41

such as the one above, where you need to calculate sizes and positions based on values that aren't set until an element is rendered

alandipert19:04:43

the idea of attributes being cells is very attractive to me also

alandipert19:04:58

i feel like it's compatible with clojure to the extent that the reference to the object that has the attributes is immutable

alandipert19:04:46

i guess that's still problematic, because what if you dissociate an attribute and someone held on to a cell view of it

jumblerg19:04:06

don't implement IDissoc, assoc, whatever?

micha19:04:32

seems like a lot of work to get around the awkwardness

jumblerg19:04:37

i assume theres a protocol associated with dissociation that can be implemented the way we need

micha19:04:43

tells me to look for the real solution elsewhere

micha19:04:02

we're trying to put a round thing in a triangular hole

micha19:04:12

adding more typing probably isn't what we need

jumblerg19:04:45

i mean, it isn't a map per se, it is a elem type with the appropriate protocols implemented accordingly

jumblerg19:04:28

there's a reason all these fns aren't all part of the same IMap protocol. separate comcerns.

jumblerg19:04:50

sorry for terseness. gym. phone.

micha19:04:24

are there any examples of objects as maps in clojure?

jumblerg19:04:27

we already have an Element type, just proposing to implement a few more protocols

micha19:04:39

like using the map protocols for interop with mutable java objects?

micha19:04:33

there's bean but that just takes a snapshot i think

jumblerg19:04:42

on the other hand, we're just building an immutable model behind a mutable thing

jumblerg19:04:51

cqrs/mvc in the small

micha19:04:04

like with hoplon i want the dom to be mutable

micha19:04:18

having immutable virtual doms is an impediment

jumblerg19:04:36

i was specifically avoiding referring to it as a virtual dom lol :D

jumblerg20:04:20

but regardless of whether you tuck it i to a small mcv triplet or distribute it across the larger cqrs pattern, we back mutable displays with versioned models all the time.

micha20:04:41

the data is immutable and stuff

micha20:04:51

but that is completely separate from the dom in hoplon

micha20:04:54

which is a good thing

jumblerg20:04:28

also, you're already doing this with the kids atom

micha20:04:21

yeah i don't like it lol

micha20:04:33

it's a tradeoff there for sure

jumblerg20:04:13

maybe the Element is a different mutable thing altogether, but just has the ability to fire formula cells when attributes change

jumblerg20:04:34

i haven't looked at javelin protocols much

micha20:04:42

well the first step is definitely to make an event based implementation

micha20:04:07

(div :foo-change #(...

micha20:04:48

anything you do will be based on that

alandipert20:04:08

hm what if the set of attributes on an object was its own cell

jumblerg20:04:34

yeah, like the kids cell

alandipert20:04:47

if you take a thing and make every mutable aspect of a cell, maybe you have a thing

jumblerg20:04:46

we have an elems cell/atom and an attrs cell

jumblerg20:04:23

or the elem itself implements javelin protcols?

jumblerg20:04:15

(cell= newwidth (+ 10 (:css/width elem)))

micha20:04:45

there must be a better way to do that though

jumblerg20:04:50

i don't understand how the cells are implemented though

micha20:04:52

using css

micha20:04:20

like what if the two depend on each other's widths

micha20:04:23

things like that

jumblerg20:04:50

cyclical dep?

micha20:04:27

i dunno, it seems like there must be a better way than implementing a new box model

jumblerg20:04:09

another thought

jumblerg20:04:29

as big a deal as being able to declaratively specify attributes of one elem based on others is, could also trigger cyclical infinite rerenders

jumblerg20:04:08

or maybe that was what you meant, i mean with the browser itself as 1/2 of the loop

jumblerg20:04:32

of course, i guess you run that risk anytime you do a calculation and do something stupid

jumblerg20:04:30

the box model is broken

micha20:04:00

yes but is it really feasible to make a new one in javascript?

micha20:04:17

canvas gives you a clean slate for that

jumblerg20:04:36

i was thinking about that, actually

jumblerg20:04:47

some ppl do it already

jumblerg20:04:23

maybe someone else has done one we can borrow

jumblerg20:04:18

massive undertaking though, probably better just to put a sane api over the existing one

micha20:04:27

i think with javascript as it currently exists

micha20:04:50

any kind of object property -> cell thing will need to be implemented as event handlers

micha20:04:01

so that seems like the logical place to start