Fork me on GitHub
#hoplon
<
2015-12-13
>
ul08:12:12

@micha, @alandipert: I tried to make javeline-like (in sense of being glitch-free) reactive expressions, but with two differences: * child cell/formula register itself in the parent one on deref (i took this idea from freactive.core, but implemented in glitch-free manner) * if you call a function which produces formula inside of creating another formula, it still will be interpreted as reactive input, not literally (no need to do tricks like that (snippet from one of my hoplon projects):

(defn t [k] ((formula (fn [lang] (t* lang k))) lang))

;; NOTE do not inline `c`, it prevents `text` from recognizing it as a cell
(defn ttext [k] (let [c (t k)] (text c)))
) * formulas performs self-cleanup (detach from sources) if no sinks or watches attached to them after update or watch removal — in most cases it allows to not care about manual disposal and prevents memory leaks while doing a lot of dynamic formulas I will be happy to hear any feedback and ideas

micha08:12:07

are you using this with hoplon?

ul08:12:08

ah, forgot link

ul08:12:17

no, not yet

ul08:12:57

i'm testing it with simple home-grown ui lib

ul08:12:05

it requires some thinking how to integrate to hoplon

ul08:12:29

snippet was from hoplon project demonstrating javelin which confuses me

ul08:12:50

because i need to create cell first and then pass it to text

ul08:12:02

s/cell/formula/

ul08:12:23

if i call function creating formula in-place, it is not picked up by text

ul08:12:54

this tricked me several times, so i decided to experiment with alternative implementation of frp

micha08:12:01

i don't understand the snippet

ul08:12:01

it was an attempt to integrate tower (i18n lib) with hoplon

ul08:12:35

t takes a key of message to be translated ans returns formula, which contains translation depending on lang cell value

ul08:12:08

and ttext is like text, but takes a key and returns text element with dynamic content

ul08:12:42

and when I do (defn ttext [k] (text (t k)) it doesn't work

ul08:12:04

only if I do like in snippet, with let

micha08:12:23

the tradeoff with marking cells in formulas with deref is that you need to know which things are cells

micha08:12:27

consider for example:

micha08:12:53

(defc= f1 (+ x y))

micha08:12:07

any of those can be cells

micha08:12:16

+, x, and y

micha08:12:32

this is useful when you're making components

ul08:12:52

yep, i understand, but as far as you are using f1 in handlers or in making formulas like in my snippet, you anyway should care about its nature

ul08:12:44

and sometimes being explicit is even better

micha08:12:09

i'm interested to see if you can make the autodetatchment work in a ui with async stuff

ul08:12:52

my autodetachment is kind of high probability, not guarantee

ul08:12:29

if watch removed — let's try to detach, if nobody care about cell anymore

micha08:12:33

the problem comes when you have an async process that derefs the cells without a watch etc

micha08:12:44

like a form submit handler

ul08:12:14

simple deref outside formula will not create any gc-preventing links

micha08:12:22

that's the problem, no?

micha08:12:46

oh i guess there will be references in the closure that will prevent gc

ul08:12:50

no, because if you pass formula to handler, you have your own link to it

micha08:12:53

but not autodetatch

micha08:12:00

so that would be bad

ul08:12:05

i perform autodetach on every updated cell

ul08:12:10

after walking graph

micha08:12:17

you'd stil have the thing in memory but it won't be updating anymore

micha08:12:29

consider:

micha08:12:16

(let [c (cell nil)]
  (form :submit #(doit @c)
    (input :keyup #(reset! c @%))
    (input :type "submit")))

micha08:12:32

ok there we go

micha08:12:44

would c be autodetatched?

micha08:12:27

ah but c is an input cell

ul08:12:32

from what? it does not participate in any formula and can be gc'ed. in my implementation source cells not linked from anywhere

micha08:12:42

yes this makes sense

ul08:12:55

but let replace c with lens

ul08:12:03

give me a moment to think

ul08:12:15

so, if c is lens (like (formula #(@source-cell) #(reset! source-cell %)) then it will be not autodetached automatically on form disappear. It will be detached only if and when source-cell will be next time updated

ul08:12:55

it is the weak place, yes, but it is known to me from the beginning — it is why i call it high-probability autodetach, not guarantee

ul08:12:47

as far as i can see in my apps, this case is rare — if formula has unupdatable sources for a long time and is used just in closure

micha08:12:15

i like this!

micha08:12:46

man, thanks for showing me this

ul08:12:19

thank you for discussion! if some kind of cross-pollination with javelin will happen, i will be happy

micha08:12:30

i feel like about 10 lbs of cobwebs have fallen out of my brain

micha08:12:38

i guess the tricky part is to correctly remove watches when things are removed from the dom

micha08:12:50

doable though, i'm sure

micha08:12:08

if the removal is done via the ui framework

ul08:12:32

yes, i'm thinking exactly about the same

ul08:12:49

in most cases ui framework should care about that

micha08:12:21

yeah totally

micha08:12:29

i think it's doable for sure

micha08:12:48

even in the presence of external libraries like jquery plugins

micha08:12:24

you could do the detatching then, too

micha08:12:16

follow back from the cell the watch was removed from, detatching cells that have only one reference left

ul08:12:02

yes, i'm doing it recursive:

(clean [this]
    (when (and (empty? sinks) (empty? watches))
      (doseq [source sources]
        (remove-sink source this)
        (when (satisfies? IReactiveExpression source)
          (clean source)))
      (set! sources #{})
      (set! state ::thunk)))

micha08:12:08

that's what flapjax did, i think

micha08:12:39

yeah the sinks are a sort of reference counting scheme

micha08:12:51

and the watched cells are like GC roots

micha08:12:28

if you remove a watch you can then detatch any cells that have only one sink

micha08:12:34

recursively

ul08:12:39

and what is good, that if one of the detached formulas is referenced somewhere in the code and reused again later

ul08:12:53

it will just compute and attach sources and to sources again

micha08:12:33

that's interesting

micha08:12:47

seems like magic simple_smile

micha08:12:40

haha i know what i'm going to be doing tomorrow

xificurC20:12:48

if I get (let [x (li (a))] ..) where (li (a)) is out of my control, how can I apply e.g. :href "#" to the a part via x?

micha20:12:26

you'd need to use jquery, like this

micha20:12:28

((-> (js/jQuery e) (.children "a") (aget 0)) :href "")

micha20:12:33

that should work

micha20:12:01

where e is the (li (a ... element

micha20:12:20

does that work for you?

micha20:12:33

you should be able to even have the :href value be a cell and it'll update reactively