Fork me on GitHub
#hoplon
<
2015-08-04
>
ul09:08:40

i decided to update my hoplon 6.0.0-alpha1 app to the latest hoplon

ul09:08:08

any breaking changes i'm to be aware of?

onetom15:08:23

@ul: last time micha said it's still alpha2 which is the safest to use

ul15:08:10

thanks, will try it

ul15:08:38

didn't read, but saw some passages about cardinal changes ongoing

ul15:08:09

joy to listen that hoplon is moving forward

micha19:08:35

@ul: alpha4 has some breaking changes that are somewhat subtle

micha19:08:53

they're not really API changes, but things are different underneath

micha19:08:33

with alpha4 you get the new elem+ anonymous custom element and the defining form defelem+

micha19:08:51

that's like defelem but with a fully consistent and uniform behavior

micha19:08:06

for example, with alpha2 you might do:

micha19:08:42

(defelem mylist [attr kids]
  (ul attr (map li kids)))

micha19:08:59

now mylist is a function that takes args and returns DOM elements

micha19:08:09

i could use it like this in an application:

micha19:08:35

(div
  (mylist
    "one"
    "two"
    "three"))

micha19:08:41

and that would produce

micha19:08:11

<div>
  <ul>
    <li>one</li>
    <li>two</li>
    <li>three</li>
  </ul>
</div>

micha19:08:36

however, it's not a very consistent and uniform interface because you can't do:

micha19:08:32

(def list1 (mylist "one"))

...

(div (list1 "two" "three"))

micha19:08:39

because that would poduce

micha19:08:23

<div>
  <ul>
    <li>one</li>
    two
    three
  </ul>
</div>

micha19:08:51

i.e. the mylist function returns a <ul> element, not a mylist element

micha19:08:16

it's only the arguments at the mylist call site that get wrapped in <li>s

micha19:08:43

the same problem occurs when you try to do:

micha19:08:10

(div
  (mylist
    (loop-tpl :bindings [x xs]
      x)))

micha19:08:45

because the mylist function doesn't get arguments reactively

micha19:08:52

alpha4 solves this

micha19:08:15

with alpha4's defelem+ the arguments are cells, not regular arguments

micha19:08:14

(defelem+ mylist [_ kids]
  (ul (cell= (map li kids)))

micha19:08:24

now you can do all of those things

micha19:08:26

(div
  (mylist
    (loop-tpl :bindings [x xs]
      x)))

;; or

(div
  ((mylist "one")
   "two"
   "three"))

;; etc.

micha19:08:28

the downside of this is that Element.prototype.appendChild() is being monkey-patched

micha19:08:48

which is not a problem unless you have external javascript libraries that are fishing around in the DOM and replacing things

alandipert20:08:51

so while i was on vacation i did some hammock time about dynamic cells

alandipert20:08:19

so yeah with any resource you have two options

alandipert20:08:33

manage everything manually, or opt for automatic manage… which isn’t totally automatic

alandipert20:08:52

you still have to describe to the management system the rules for release

alandipert20:08:17

in the average program language the management rules can be inferred from scoping rules in most cases

micha20:08:32

yeah we can't do that with cells

alandipert20:08:36

and in some cases not, which is where the edge cases are

micha20:08:36

because they're asynchronous

alandipert20:08:41

not necessarily

alandipert20:08:44

well scopes, no

alandipert20:08:56

so if we want automated-something we need to figure out how to inform the thing

micha20:08:58

we could do a haskellish thing

micha20:08:13

where cells are lazy until they are connected to a side effect

alandipert20:08:59

i think maybe ‘strong’ and ‘weak’ are the terms

alandipert20:08:04

weak cells are open for collection

alandipert20:08:07

strong cells never are

micha20:08:13

excellent, yes

micha20:08:20

a dynamic binding even

alandipert20:08:27

but yeah there are reasons to make strong cells even without io

alandipert20:08:38

like what we do sometimes with setting inputs in a watcher

alandipert20:08:40

to jump the graph

micha20:08:46

weak cells must always be hooked up to a side effect

micha20:08:51

or they get reaped

micha20:08:58

probably on watch removal

micha20:08:13

recursively reap till reach first strong cell

micha20:08:41

i think this could totally work

micha20:08:08

because the things you want to be able to GC are usually created by procedures, not explicitly

alandipert20:08:33

so i think the way to doing is with ::string meta

micha20:08:44

so you could just do like ` (binding [weakness true]

alandipert20:08:12

i’m not sure about how to relegate the 2 ways

alandipert20:08:18

a new namespace feels like a good first step

alandipert20:08:29

to experiment anyway

micha20:08:34

i don't think you want different names

micha20:08:54

because the code that's constructing cells shouldn't need to choose which type

micha20:08:59

that's the expression problem

alandipert20:08:04

what’s the default type?

micha20:08:11

the default is strong perhaps

micha20:08:33

i was imagining like in loop-tpl for example

micha20:08:21

(loop-tpl :binding [x xs]
  (binding [*weak* true]
    (li :color (cell= (if somecell? "red" "blue")) x)))

micha20:08:45

now we know that the anonymous cell there has a watch on it, set by hoplon

micha20:08:05

so that could be the refence counting part

micha20:08:09

*reference

micha20:08:58

and here loop-tpl is managing the removal of elements from the DOM

alandipert20:08:18

so dependers on weak cells are themselves weak you were saying?

alandipert20:08:41

or weakness is contagious

micha20:08:10

(let [alloc (atom #{})]
  (binding [*allocated-cells* alloc]
    (li :color (cell= (if asdf? "red" "blue")) x)))

micha20:08:15

what about that?

micha20:08:33

then the cell constructor would swap! on *allocated-cells*

micha20:08:51

and the scope that allocated the atom can manage cleanup later

micha20:08:04

when it removes the things from the DOM or whatever

micha20:08:31

i mean the cell constructor would add a reference to ever new cell to *allocated-cells* if it's bound to an atom

micha20:08:53

then an external manager can reap them

alandipert20:08:34

i’m not sure

alandipert20:08:58

it seems like the ideal thing is metadata ona cell that informs the machinery to disappear it when the machinery is the last reference

micha20:08:38

but what constitutes a reference?

micha20:08:55

consider:

micha20:08:17

(with-interval 1000 (.log js/console @some-cell))

alandipert20:08:06

right, it’s for the user to decide somehow

alandipert20:08:18

and that decision needs to be visible somehow since scope doesn’t cut it

alandipert20:08:32

(def some-cell (cell 123 :meta {:strong true}))

micha20:08:12

and i guess you'd set a worker thread basically to perform GC periodically?

micha20:08:23

or perhaps probablistically

alandipert20:08:28

actually in that case some-cell would never go away because it’s named

alandipert20:08:37

so js would not gc it

alandipert21:08:06

maybe the thing to research is the relationship between gc and closures that do io in v8

micha21:08:36

ok take this example then

micha21:08:55

(.onEvent foo #(.log js/console @some-cell))

micha21:08:45

i guess that's the same thing

micha21:08:34

in the example with the (li :color (cell= ...) ...) above

micha21:08:02

the only thing that makes that cell a candidate for collection is removing the li from the DOM

micha21:08:22

and removing it with no intention to add it back in later

micha21:08:57

it would be nice if we could know what else is referencing the li element

micha21:08:07

and if we're the last thing we destroy it

micha21:08:19

but i don't think that's possible

micha21:08:49

so it seems like we have two different approaches

alandipert21:08:55

maybe for loop-tpl go through a reference-counting allocator?

alandipert21:08:11

the central thing is definitely io

micha21:08:14

yeah that's what i was thinking with the *allocated-cells* thing

micha21:08:32

like if you're managing the lifecycle of a thing

micha21:08:40

like you have to do with the DOM

micha21:08:02

creating new things and inserting them somewhere, then removing them later and expecting them to be collected by GC

micha21:08:17

whoever allocates them needs to finalize them

micha21:08:38

it's interesting that dynamic allocation is pretty well isolated to loop-tpl and whatnot

micha21:08:21

so i feel like we can figure a way to use that to transparently manage dynamic allocation of cells, too

micha21:08:00

what about making like a "crack" or a fissure between strong and weak subtrees of the dep graph?

micha21:08:23

like instead of the cells themselves being strong or weak, it could be the connection between the subtrees that is strong or weak

micha21:08:51

like what if each cell's .-sources was always a weakmap or something that doesn't hold a strong reference in the VM

micha21:08:09

and then it could have two different .-sinks, one strong and one weak

micha21:08:18

then i think you could set a boundary around the GC eligable cells in the graph by adding formulas to the weak .-sinks of the dependency cells

micha21:08:47

so in the case of the DOM it would be js doing the reference counting for us

alandipert21:08:25

yeah i think that division of sources and sinks is the way to do it

alandipert21:08:45

i’d be curious to hear about the kinds oft hings @ul is needing dynamism for

alandipert22:08:09

i imagine the use case would always be io to the screen somehow

alandipert22:08:27

generating formulas to back temporarily visible parts of the output

alandipert22:08:40

like using cell= for decorating a string in your example