Fork me on GitHub
#hoplon
<
2016-12-30
>
puzzler00:12:18

I've tried dozens of ways now to implement the idea of an edit cell that dynamically points as a cursor into different parts of a data structure. Some showed promise, but none have worked out after more thorough testing. I haven't been able to crack this yet.

puzzler00:12:20

I find the idea of storing a lens in a cell to be the most promising, but the way that cells are unwrapped before passing them to the formula has made it really difficult to express the notion of derefing the outer cell while leaving the lens stored inside intact. Furthermore, it's hard to make sure everything gets updated when it is supposed to in that case.

puzzler00:12:50

As for the idea of destructively mutating some cell into the lens-cell of choice, I haven't gotten set-lens! working well enough yet.

micha01:12:34

@puzzler i am working on some changes that will possibly merge tonight/tomorrow/soon

micha01:12:13

one change is adding an arity to set-formula! that accepts the update fn

micha01:12:16

this update has a bunch of performance related changes

micha01:12:57

but the idea is that you could do (set-formula! a-lens f sources updatefn)

micha01:12:31

or with the macro like this (set-cell!= a-lens (some expr) #(the update fn))

micha01:12:50

to mutate the lens in place, or make a lens out of a different type of cell

micha01:12:04

by mutation

micha01:12:20

lol also docstrings finally

micha01:12:59

perf is looking good, tests run in 50-60ms now, down from 150-200ms before

puzzler03:12:07

When the test cell in a when-tpl changes (for example from one truthy value to another), does the body definitely get re-evaluated, or only if the test changes from a truthy value to a falsey value or vice versa?

candera03:12:07

@puzzler I don’t know, but gosh I would hope it re-evaluates. Easy to test, though: (when-tpl whatever (println “evaluating”) some-value)

alandipert03:12:43

pretty sure nothing happens if the value remains truthy, per https://github.com/hoplon/hoplon/blob/master/src/hoplon/core.clj#L174

alandipert03:12:04

since the action is formula, not watch based

candera03:12:23

Just tested this code:

(let [c (cell "hi")]
     [(when-tpl c
        (span c))
      (button :click #(reset! c nil) "nil")
      (button :click #(swap! c str "x") “x”)])

candera03:12:32

And the button clicks both change the span.

candera03:12:43

But I guess that’s not necessarily the same thing.

candera03:12:04

Ah, I see that it is not.

candera03:12:28

So, I’m not sure what’s happening, but it’s definitely not evaluating the body, even when the body does change from true to false. 🙂

puzzler03:12:41

OK, that's what I thought, I just wasn't sure that was what I was observing.

alandipert03:12:25

i think i see, and yeah that's weird. the cell is in there, changing

alandipert03:12:34

makes sense tho

alandipert03:12:37

it's what cells do

micha03:12:40

the when-tpl form produces a cell containing a vector of elements

micha03:12:10

when the predicate is false the element is removed from the vector and kept in the pool outside of the dom

micha03:12:26

when the predicate becomes true the element is conjed back onto the vector in the cell

micha03:12:44

the main idea there is that the body is evaluated only once

alandipert03:12:51

but when the element has cell-backed attr or children, those continue to change, whether or not it's attached to dom, right?

alandipert03:12:58

ie it's not like cells in their are disconnected

micha03:12:09

that's the objective

micha03:12:27

so the element can be restored to the dom later without needing to "catch up" its state

micha03:12:33

because its state is eternal

micha03:12:10

the element returned by the body of the when-tpl continues to live even when the predicate is false

micha03:12:16

although it is removed from the dom

micha03:12:36

and when the predicate becomes true again the same element is restored to the dom

micha03:12:49

a new element is not created because the body will never be re-evaluated

micha03:12:58

when-tpl is useful to lazily allocate dom resouces, because the body doesn't get evaluated until the predicate becomes true

puzzler04:12:02

Is there already a macro that causes a body to be re-evaluated whenever a given cell changes? I know how to write one in terms of formula, I just want to know if it already exists.

micha04:12:38

(defc c 100)

(when-tpl (cell= (odd? c))
  (do
    (.log js/console "body evaluating")
    (div "i am here!")))

(with-interval 1000 (swap! c inc))

micha04:12:04

that will print "body evaluating" only once for the lifetime of the application

puzzler04:12:33

Oh, that's a subtle point I hadn't realized.

micha04:12:35

@puzzler yes, if you want the body to evaluate each time you just use a cell

puzzler04:12:03

Well, a formula cell walks the expression to figure out the sources. I want to specify the source.

candera04:12:29

I wrote it because I find code-walking creepy.

micha04:12:36

ah yes, how do you feel about adding that to javelin.core @candera ?

candera04:12:46

But I see the value of the -tpl functions.

candera04:12:57

@micha I feel really awesome about someone doing that. 🙂

micha04:12:07

ok i will doit

candera04:12:29

:thumbsup: I love how I return the love of you creating Hoplon by giving you work to do.

micha04:12:46

haha cut and paste is my specialty

candera04:12:54

Seriously, if it’s easier on you I can create a PR or whatever.

candera04:12:00

But it won’t be tonight or anything.

micha04:12:06

haha no worries

micha04:12:34

feel free to suggest changes or whatever, it'll be marked alpha or something for a little while

micha04:12:46

in case we want to shed bikes

alandipert04:12:01

re: doing something every time a cell changes value, there's always add-watch if you want to skip the rails a bit

candera04:12:05

I have used it a lot, and it’s pretty stable in my code, but I’m a relative Hoplon/Javelin n00b.

alandipert04:12:27

@candera our community is small, you're probably top 20 world wide hoplon expert 😄

micha04:12:30

add-watch is good if you have only one cell you depend on

puzzler04:12:49

Yes, formula-of is exactly the kind of thing I've been wanting.

micha04:12:55

actually i guess the ideal thing is make a formula cell and then add a watch to that

candera04:12:29

Is there a reason to use a watch instead of a formula with side effects?

micha04:12:52

hoplon will be adding a watch if you add a cell as a child of an element

puzzler04:12:54

I assume the distinction is that watch is what you'd use if you wanted to pick up on something being set to the same value twice.

alandipert04:12:04

for awhile i did it just to communicate that the action was side effects

micha04:12:24

adding a watch is less work than creating a cell

alandipert04:12:25

but formula/cell= are shorter to type so i usually just do those now

micha04:12:34

i mean less cpu cycles/memory

micha04:12:39

not less human work

candera04:12:06

If you need the formula cell anyway, though, I presume there is no additional cost?

candera04:12:21

OK, good to know.

candera04:12:31

Off to fly imaginary airplanes through pretend weather.

micha04:12:46

i have been watching youtube videos of this game IL-2

micha04:12:50

really interesting

micha04:12:01

the graphics are insane

candera04:12:10

Flight sims are an amazing corner of the internet.

candera04:12:59

It’s literally a hobbiest-augmented fork of a 20-year-old game.

puzzler04:12:10

An observation about formula-of is that it doesn't support the lens arity with an update callback, because it allows for multiple bodies.

micha04:12:30

i watched some videos of people playing as nazis shooting down b-17s

thedavidmeister04:12:41

@micha @candera i’m building up elems as i go in a gallery and the amount of global stuff like logging and metrics i need is not huge

micha04:12:44

the graphics were pretty realistic, it was kind of depressing

candera04:12:26

@micha Yeah, I make it a point to remember what it’s simulating is deadly real.

micha04:12:31

it's hard to not imagine humans in there

micha04:12:48

with the contrails and all that

micha04:12:38

but then they drop bombs and i was like fuckem

candera04:12:16

@puzzler I feel like something along the lines of (with-setter (fn …) (formula-of …))) would keep those pieces nicely orthogonal without creating some horrible syntax nightmare.

candera04:12:29

I think maybe that already exists?

micha04:12:14

i could exist very easily 🙂

candera04:12:16

@micha I fly with a bunch of guys who are actual current or former military pilots. Bunch of civilian pilots as well.

micha04:12:20

i mean it could

candera04:12:00

The name with-setter is terrible, but hopefully the idea is obvious.

alandipert04:12:02

i wonder if formula-of should assert the things are cells?

alandipert04:12:12

since the point of it is to be explicit about the cells you're using

micha04:12:26

they don't need to be cells though

candera04:12:31

I guess it’s not terrible to have (formula-of [foo bar :set (fn …)] …). That matches let/for/doseq syntax, and I believe it’s unambiguous.

candera04:12:55

But that with-setter thing would be generally useful.

alandipert04:12:56

oh, i guess i miss the point of it

candera04:12:09

Well, I can’t think of a time I’ve used it without cells.

micha04:12:41

if you restrict it to cells then you end up not able to use it in a place where you are passed in the things you are depending on

candera04:12:45

It’s a pretty simple expansion against formula.

micha04:12:56

like for example i can do (div :toggle true ...

micha04:12:07

or (div :toggle (cell= ...

alandipert04:12:08

yeah, so it sohuld probably retain the formula semantic and do the deref* thing i suppose

alandipert04:12:11

be cool with non-cells

micha04:12:25

the formula machinery will be doing that anyway

candera04:12:53

Yea, IMO the point is to be minimal sugar over formula. I don’t think it should have much in the way of its own semantics outside of that.

micha04:12:33

maybe there is a way to optimize there

micha04:12:47

because we know at cell construction time which things are cells and which arent

alandipert04:12:59

yeah that's what i was thinking about the assert, it would happen once

alandipert04:12:17

seems like a useful angle... for something

micha04:12:44

seems like making that array every time is wasteful

alandipert04:12:56

also doing the deref* every time

micha04:12:56

but i can't think of how to effectively precompute that

alandipert04:12:39

i guess deref* hardly does anything tho

micha04:12:41

everything is just plain js collections now internally, much faster

micha04:12:16

sources, sinks, etc., except for the watches map, which needs to be able to have keys that aren't strings

puzzler04:12:22

I want to make sure I'm not missing something here. I pasted in the formula-of macro, and have something like this:

(when-tpl my-formula-cell
  (formula-of [my-cell]
    (div "some stuff I want to be visible when my-formula-cell is truthy and update when my-cell changes")))
but I'm getting this error:
Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
    at core.cljs:94
    at hoplon$core$merge_kids (core.cljs:93)
    at G__9443__2 (core.cljs:4087)
    at G__9443 (core.cljs:4089)
    at core.cljs:48
    at cljs.core.Atom.cljs$core$IWatchable$_notify_watches$arity$3 (core.cljs:4213)
    at Object.cljs$core$_notify_watches [as _notify_watches] (core.cljs:673)
    at Object.cljs$core$reset_BANG_ [as reset_BANG_] (core.cljs:4254)
    at Function.cljs.core.swap_BANG_.cljs$core$IFn$_invoke$arity$4 (core.cljs:4273)
    at cljs$core$swap_BANG_ (core.cljs:4258)
Does that mean anything to anyone?

micha04:12:29

i don't think you want to nest cells that way

micha04:12:35

the when-tpl returns a cell

micha04:12:46

and the body of it should produce a dom element

micha04:12:00

the body of your when-tpl there produces a formula cell

micha04:12:16

so what hoplon sees is a cell containing a cell

puzzler04:12:30

I thought it might be something like that, so I tried wrapping the second formula in a div, but I got the same error.

puzzler04:12:32

(when-tpl my-formula-cell
  (div
    (formula-of [my-cell]
      (div "some stuff I want to be visible when my-formula-cell is truthy and update when my-cell changes"))))

micha04:12:48

seems like that would work

micha04:12:02

or should work

alandipert04:12:49

@micha how are you doing the javelin perf tests, just time boot test-javelin?

micha04:12:06

i am running the tests like 10 times and timing each one in cljs

micha04:12:36

lol not too fancy

alandipert04:12:59

reduce comp dude

alandipert04:12:10

:dove_of_peace:

micha04:12:17

vim did it before i could think of the clojure way

micha04:12:24

i was like "what about..."

micha04:12:30

and vim was already smoking a butt out back

alandipert04:12:11

was there some bug with the new perf version?

micha04:12:28

i think there was something wrong with the hoplon perf changes

micha04:12:45

javelin should be good, but there might be something lurking

micha04:12:26

it would be good to have something that measures like "cells updated per second" or something

micha04:12:05

with like some minimal formula like (+ a b) so the javelin time dominates

micha04:12:38

also "cells created per second" would be good to measure

micha04:12:04

as it is now whenever i add new tests the performance metrics are reset

candera04:12:19

@puzzler I think what you’re describing is exactly what I posted above, except i used the same cell for both.

candera04:12:58

I.e. this:

(let [c1 (cell "hi")
         c2 (cell true)]
     [(when-tpl c2
        (.log js/console "Evaluated")
        (span c1))
      (button :click #(swap! c2 not) "toggle")
      (button :click #(swap! c1 str "x") “x”)])

candera04:12:03

Which works as I would expect.

candera04:12:28

I mean, as I would expect after the conversation tonight. 🙂

candera04:12:03

Anyway, I am an old man and must go to bed. Later, all.

puzzler04:12:51

So, for some reason, the formula-of macro is triggering errors for me in situations where the manual rewrite to formula is not. I don't have a good explanation for why right now. The macro looks right to me, but I just wanted to mention that I'm getting strange results with it, since at some point you plan to put this in core.

puzzler04:12:54

For example, I mentioned above that wrapping div around it didn't help, but it did once I rewrote the formula-of macro manually.

alandipert04:12:08

that's surprising

puzzler04:12:26

yes, I don't have a good explanation. Just throwing it out there as a data point for now.

alandipert04:12:13

(when-tpl my-formula-cell
  (div
    ((formula
      (fn [_]
        (div "some stuff I want to be visible when my-formula-cell is truthy and update when my-cell changes")))
     my-cell)))

alandipert04:12:18

^^ is similar to what you tried that worked?

micha04:12:18

maybe there was some stale state in the cljs compiler?

puzzler04:12:27

These should be equivalent right?:

(formula-of [editing] (and editing (:type @edit-path)))
((formula (fn [editing] (and editing (:type @edit-path)))) editing)

micha04:12:28

did you try restarting your build?

puzzler04:12:39

I did. But I can try again.

micha04:12:50

when messing with macros it tries to patch the runtime state as best it can

micha04:12:03

but i can imagine that going wrong from time to time

alandipert04:12:45

i wonder if it's the capturing

micha04:12:52

and yes, those look equivalent to me

alandipert04:12:30

i guess in both cases, the cell is editing and the parameter is editing

alandipert04:12:37

in the past cljs has had bugs around situations like that

alandipert04:12:50

but if that theory was correct both wouldn't work

puzzler04:12:10

Yeah, restarted build and still getting strange results from the macro.

puzzler04:12:21

Let me try renaming...

micha04:12:28

(fn= [x y] (+ x y))

micha04:12:37

what think

alandipert04:12:19

i like but think formula= would be more consistent-ish?

micha04:12:20

(defn= foo [x y] (+ x y))

alandipert04:12:24

so long though

micha04:12:40

the fn is because of the syntax being identical

micha04:12:54

like fn and fn= have identical syntax

micha04:12:11

or whatever we call the stuff macros expect

alandipert04:12:13

i like it now

alandipert04:12:38

it's not like it support multiple arity

alandipert04:12:52

so i'm not sure how much like a function it actually is

puzzler04:12:54

This version worked (using the map option for renaming):

puzzler04:12:02

(formula-of {e editing} (and e (:type @edit-path)))

puzzler04:12:36

So, most likely explanation is something subtle with the way the macro is capturing the global name, I guess?

micha04:12:00

i think we can make it work

alandipert04:12:17

yeah, my theory is

alandipert04:12:34

cljsc isn't resolving the outer editing in a namespace, and so compiles it into something like window['editing']

micha04:12:37

we want to bind to gensyms anyway in the macro probably

alandipert04:12:48

yeah :thumbsup:

alandipert04:12:05

actually scratch that, if the name wasn't defined, js would complain differently

micha04:12:15

man i just realized the syntax is not the same at all

micha04:12:24

it's like exactly inverted

micha04:12:32

very weird

micha04:12:44

both the binding and the arguments

micha04:12:47

at the same time

micha04:12:55

super strange business

micha04:12:08

i like how weird it is

alandipert04:12:09

it reminds me of the original idea, the revers op

micha04:12:15

haha right

micha04:12:21

cells are weird

alandipert04:12:23

(lift a b c +)

alandipert05:12:09

i read a kind of related thing in the graham lisp book

alandipert05:12:41

he talks about why CL let is parallel

alandipert05:12:07

the justification was apparently, let is lambda, and it's not like parameter know about eachother

micha05:12:15

they have set! which makes let less needed for mutating things

alandipert05:12:24

to achieve sequential let you need to nest lambdas

micha05:12:45

like in clj you need to do (let [x (+ y z), x (/ x 2) ...

micha05:12:03

if you want to do like what you'd do in C

micha05:12:13

where you mutate a variable

micha05:12:28

cause you don't have set! really

alandipert05:12:50

iterative shadowing

alandipert05:12:55

as crazy as set! imo

micha05:12:01

yeah i find it easier to reason about some times

micha05:12:17

especially when i'm translating an algorithm from knuth or whatqever

micha05:12:27

and i want to see it look similar

alandipert05:12:52

because of things like this i find myself more attracetd to the idea

alandipert05:12:59

of functions just having one argument

alandipert05:12:08

i think the only reason lisp doesn't work that way is syntactic convenience

alandipert05:12:17

but it would be simpler if it did

micha05:12:14

an interesting thing ^^

micha05:12:45

the @param docstring thing doesn't work with anonymous functions though, naturally

alandipert05:12:59

can't do (x (inc x)) because then x would be shadowed if it were in enclosing scope, so the best you can do seems like ((x) (inc x))

alandipert05:12:15

which is a thing graham proposes

micha05:12:36

is x a function?

micha05:12:43

how do you inc a function?

alandipert05:12:44

sorry this is for (fn [x] (inc x))

alandipert05:12:01

ramblings about simpler ways of conveying it

micha05:12:35

reminds me of the original hlisp

micha05:12:50

where everything was a list, including atomic things

micha05:12:58

remember that?

alandipert05:12:22

1958 lisp worked that way too tho, i do know

alandipert05:12:40

err, it had symbols

mynomoto12:12:20

@dm3 @thedavidmeister I did integrate devcards to be able to use it with Hoplon on github-client but I don't see the advantage in relation to just display the component. Is there one?

dm312:12:22

not sure I understand what you mean

thedavidmeister12:12:02

@mynomoto does it do the forwards/backwards state thing?

mynomoto12:12:04

So devcards allow you to display the components in a page so you can have visual feedback. What I'm not seeing is the advantage in relation to just show the component.

thedavidmeister12:12:27

devcards has a few features (that i’m not sure if are compatible with hoplon)

thedavidmeister12:12:39

- state history - replay, forwards, backwards, etc.

mynomoto12:12:52

@thedavidmeister do you have a link for that?

thedavidmeister12:12:55

- dynamic updating of the bit that shows you the values passed in

thedavidmeister12:12:04

- integration with cljs tests

mynomoto12:12:15

I just made the card appear on screen 😉

dm312:12:38

it's totally possible with hoplon, you just have to hook up into cells

thedavidmeister12:12:41

the bmi calculator gives you the gist of what i’m talking about

thedavidmeister12:12:55

- markdown integration

mynomoto12:12:09

Interesting.

mynomoto12:12:38

I will try a more advanced integration.

thedavidmeister12:12:01

@dm3 i know that in theory cells should support something like this just fine, i mean, i’m not sure what needs to be done to make devcards “hoplon aware"

mynomoto12:12:17

But I have the state history on the app itself so not sure if this is a big advantage...

thedavidmeister12:12:43

@mynomoto because working directly on the app is more work than working on a single element

dm312:12:46

I still think doing a Hoplon-specific devcards will be better 🙂

mynomoto12:12:20

@thedavidmeister yeah, but I don't think it's hard to plug that on the component level.

thedavidmeister12:12:57

well then it should be trivial to spin up an awesome devcards clone 🙂

thedavidmeister12:12:33

the advantage is having hundreds of these examples ready to go, and them being standardised/configuration driven

thedavidmeister12:12:44

just to minimise friction and maximise the chance of it being used

mynomoto13:12:20

I will play with both options.

dm313:12:25

Micha wrote a bit about his vision of composable components yesterday too

dm313:12:32

like a component is wired through a set of dynamic (params) + static (vars) cells

dm313:12:16

I guess if we have some sort of component spec thing, we'd be able to pick that up from devcards to allow playing with the inputs dynamically

thedavidmeister13:12:22

i don’t really understand how this “component wiring” is different to what i’m already doing 😕

thedavidmeister13:12:29

could you give me more info @dm3 ?

thedavidmeister13:12:26

as in, what’s the difference between that and passing in cells to defelem as attributes?

dm313:12:07

well, that's how you define the component (elem)

dm313:12:38

but then you want to allow devcards automatically discover what the possible inputs and their types

thedavidmeister13:12:02

oh, so like a dependency injection thing?

thedavidmeister13:12:03

or just like, listing possible options in docs?

dm313:12:57

no, like

(require '[clojure.spec :as s])
(s/def ::size int?)
(s/def ::width (cell-of ::size))
(s/def ::height (cell-of ::size))
(s/def ::thing-opts
  (s/keys :req-un [::width ::height]))

(s/fdef thing :args (s/cat :opts ::thing-opts, :elems :hoplon/elems) :ret :hoplon/elem)
(defelem thing [{:keys [width height]} kids] ...)
Something like this, just hidden below some sugar

dm313:12:17

not sure how cell-of would work with clojure.spec yet

dm313:12:03

then you'd need to say

(devtools/input ::size
   (ui/number-input))
or something

dm313:12:20

so that it'd create a number input for width and height

thedavidmeister13:12:41

oh, with sliders like unity?

dm313:12:39

but the key is to define an inspectable specification for the element

dm313:12:26

clojure.spec is nice because it's a standard solution which might have more tooling built by other people in the future

thedavidmeister13:12:38

ok, so a better version of what i do with assert on cells atm

thedavidmeister13:12:23

pre and post don’t work on defelem so...

dm313:12:05

time to write defspecelem 🙂

dm313:12:09

a good thing is spec doesn't fail on additional stuff in the map, so in the above example if you have more stuff in the {:width, :height} opts map it will still pass

dm313:12:22

so you can still pass opts to child elems

dm313:12:09

then the remaining part is also specifying static cells that the component relies on

dm313:12:25

it's basically a dependency

thedavidmeister13:12:41

i would very much like a standardised way to set what should be in the cells

thedavidmeister13:12:48

especially as the cells are dynamic

thedavidmeister13:12:57

they might be fine now but not later

thedavidmeister13:12:05

would you put the spec on the cell too?

thedavidmeister13:12:13

or just the elem

dm313:12:39

I see no reason not to

thedavidmeister13:12:48

so i could have an “int cell"

mynomoto13:12:54

I worry that spec don't play nice with datascript. No way to spec a db like a big map. You can always get entities but only those would be spec'ed

thedavidmeister13:12:20

@mynomoto i think that would be what having a schema would do

thedavidmeister13:12:28

that’s how datomic does it

thedavidmeister13:12:34

but datascript is missing that

dm313:12:37

you wouldn't spec the datascript map

dm313:12:47

but you could spec the query cells

mynomoto13:12:03

@dm3 I think that would work but you could have wrong data on db, which you could make impossible with the map in a cell approach.

dm313:12:22

yeah, but that's your input validation problem then

dm313:12:28

you should spec functions that transact the data

thedavidmeister13:12:04

@mynomoto that’s a problem right now with datascript though

thedavidmeister13:12:08

don’t think hoplon can solve it

thedavidmeister13:12:22

also, entities don’t play nice will cells at all, in my experience

mynomoto13:12:46

@thedavidmeister you need some tricks. Check the javelin.datascript ns on github-client.

mynomoto13:12:49

The problem is that entities always compare equal so you cannot put it on a cell because it will never trigger an update.

mynomoto13:12:30

But you can do touch or select-keys

thedavidmeister13:12:08

what about getting the conn from the entity?

mynomoto13:12:32

Well I don't do that, I always get that from the context which is passed to every part of the app.

mynomoto13:12:52

There is a way

mynomoto13:12:26

(cell= (do db (d/entity ...))) would do the trick I think

mynomoto13:12:13

Actually the inc trick is needed.

mynomoto13:12:28

The above won't help

dm313:12:49

so how do you get around storing entities in cells?

dm313:12:06

I just always stored value resuts

mynomoto13:12:06

It's a bit convoluted.

mynomoto13:12:35

On db transact you inc another cell, a simple counter.

mynomoto13:12:59

When you create your entities you do (cell= [entity counter])

mynomoto13:12:54

And to use (cell= (key (first entity-cell)))

dm313:12:30

it probably makes sense to have the entity pulled out into the cell always

mynomoto13:12:49

Actually (cell= [entity db]) is better

mynomoto13:12:54

No counter necessary

dm314:12:06

but then it's going to recompute on any db change

dm314:12:12

even if it's unrelated

dm314:12:44

what's lazy?

mynomoto14:12:53

The entity right?

dm314:12:13

but the cell graph isn't

dm314:12:22

so if anything depends on that, it's going to get recomputed

mynomoto14:12:41

It's the same using queries no? Db changes every query is recomputated?

dm314:12:59

yeah, but then if query results don't change, it doesn't propagate down the cell graph

dm314:12:14

if db is a cell of the Db, then it always will

mynomoto14:12:15

But only on the entities usage.

mynomoto14:12:31

Not downstream of that

dm314:12:42

I guess what I'd want to do is to (defentitycell= conn eid) => (defc= (let [e (d/entity (d/db conn) eid)] [e (:version e)])

dm314:12:16

yeah, it probably doesn't matter that much

dm314:12:39

here every transact needs to increase the version

dm314:12:10

does Datascript support dbfns that could do that automatically?

mynomoto14:12:00

Not sure, but I think having our own entity function works.

mynomoto14:12:29

I'm not sure if I understood, doing what automatically?

dm314:12:48

increasing version on an entity

dm314:12:18

there's [:db.fn/call my/inc-version eid]

dm314:12:25

which could be appended to every tx

mynomoto14:12:01

If you are doing on every tx what is the advantage in relation to use the db itself as key?

dm314:12:15

you only increase version on that particular entity

dm314:12:26

so the propagation stops there

mynomoto14:12:30

There should be a way using metadata maybe?

mynomoto14:12:38

It should be doable.

dm314:12:06

well, the entity is probably replaced (merged to create a new map)

dm314:12:38

or rather, a Datom is either added or retracted

dm314:12:55

or you mean like metadata on the ID datom?

mynomoto14:12:43

Something the tx of the last thing that changed on the entity

dm314:12:35

but for the cell to change we still have to change its value

dm314:12:53

so it can't be just (cell= entity) anyway

mynomoto14:12:40

Yeah, I was thinking that for your version idea.

thedavidmeister14:12:14

as a way to not have to pass conns and entities together

dm314:12:37

I don't think Datascript supports that

dm314:12:43

there's no history there

thedavidmeister14:12:04

it does support it

thedavidmeister14:12:34

80% sure i started using it but had to bail because of javelin

dm314:12:28

that returns the database, yes

thedavidmeister14:12:27

i can’t do (cell= (q […] (entity-db e))), etc.

thedavidmeister14:12:49

if i could, i’d almost never pass conns around, i’d use entities instead

thedavidmeister14:12:02

usually my UI makes more sense in context of entities than entire databases

thedavidmeister14:12:08

at least, at the elem level

dm314:12:19

why can't you use that? Because e is a cell?

thedavidmeister14:12:33

because e doesn’t change

thedavidmeister14:12:53

that will never recalculate

dm314:12:03

so there are 2 solutions: the one above with entity versions or making an JavelinCellEntity which always wraps Datascript Entity

dm314:12:13

and defines a different equality impl

thedavidmeister14:12:43

i don’t really understand how entity versions help

dm314:12:45

which is really one solution -> make a wrapper over datascript API where the entities placed into cells are always something that doesn't compare on just eid for equality

thedavidmeister14:12:13

because the eid would still be the same, no?

dm314:12:25

that's really the definition of the entity

thedavidmeister14:12:27

even if you added a version attribute

thedavidmeister14:12:55

so the only solution is a wrapper then?

dm314:12:33

if you want to store entities in cells - yes

dm314:12:09

you need a JavelinDatascript integration of some sorts

dm314:12:14

it may also store the last Datom transacted for that entity instead of version

thedavidmeister14:12:16

is there a way to pass to a cell a different function to use for value propagation?

thedavidmeister14:12:41

like, could i tell the cell to use touch?

dm314:12:58

but it could be possible

dm314:12:42

so you'd have a special cell type. I see that as a more complicated approach as we're playing with Javelin's semantics

thedavidmeister14:12:02

hmmm, i think i need to think about this more when i have the bandwidth and am actually working on something that could benefit from it

mynomoto14:12:06

Could we overwrite entity equality semantics on runtime?

dm314:12:21

that could break all sorts of things

dm314:12:36

s/could/would

candera14:12:19

If you have a source of data that’s not a cell, but you want to react to it, you’re going to have to do the work to eventually change a cell. Seems to me what you need is the adapter layer that watches not-a-cell and eventually calls reset!. Given that you need that, I’m not sure I see the advantage to considering approaches that mess with the semantics of cells.

candera14:12:38

(watch-db conn query cell)

candera14:12:02

Of course, making that efficient is a whole other story, but that’s a problem that would need to be solved regardless.

candera14:12:47

I suspect that something like core.logic could be helpful there, as you essentially need to run the query in reverse.

mynomoto14:12:56

@dm3 not sure if that's so disruptive by looking at the source.

mynomoto15:12:42

Also the max tx of an entity as a version seems also possible.

candera15:12:02

If you need cascading version semantics, it won’t.

mynomoto15:12:14

If that's a good idea it's other matter 😉

candera15:12:24

Person -> hand -> finger -> rings. Change rings, person doesn’t version.

mynomoto15:12:21

refs are not allowed 😞

mynomoto15:12:51

In that case db as a version doesn't look so bad

candera15:12:59

I believe your granularity is actually a query.

candera15:12:08

You want to ask questions and know when the answer changes.

candera15:12:19

That is a solvable problem, but I don’t think it’s easy.

candera15:12:31

For small data, though, just run the query again.

dm315:12:43

this seems to have relevant discussion: https://github.com/tonsky/datascript/pull/12

candera15:12:42

Yes, seems relevant. Including the part where they question whether Datascript is the right tool. 🙂

mynomoto15:12:57

Well it helps with normalization, but it depends on your data as everything usually does. Tradeoffs.

dm315:12:01

the answer is probably to keep the Dbs small 🙂

dm315:12:15

rerun queries on db change

dm315:12:34

and don't store Entity in cells, but rather pull '[*] them out

candera15:12:26

The good news is that if you’re putting the results of your queries into cells, although you need to run the queries every time, you won’t redo any work beyond that.

mynomoto15:12:31

There is d/touch for that.

dm315:12:48

does touch get components?

candera15:12:51

d/touch is generally considered to be dev-time only.

candera15:12:01

@dm3 It does in Datomic.

candera15:12:30

But generally if you’re using touch other than at the REPL you’re doing something wrong.

candera15:12:11

I would say pull [*] is a similar smell, unless what you’re writing is some sort of super general grid that doesn’t know anything about the data.

dm315:12:21

yeah, I guess that rule is to just get whichever part of the entity you care about at that point in the formula cell

dm315:12:01

but touch seems fine on the client-side

mynomoto15:12:50

Yeah, for some of my entities d/touch is fine because they have only a couple of fields that are shown on the same context but that could derail quickly.

dm315:12:57

still, you could start with that and move to querying parts when it becomes slow

micha15:12:03

is there a name for the "false pairs" in idiomatic clojure syntax, like the binding pairs in let?

micha15:12:47

or key/value pairs in a map literal

dm315:12:39

I usually refer to them as a "binding" when they're in the bindings place

dm315:12:24

or a map entry when in a map, but that works on the value level

micha15:12:37

hmm bindings could work

micha16:12:36

boot.user=> (doc for)
-------------------------
clojure.core/for
([seq-exprs body-expr])
Macro
  List comprehension. Takes a vector of one or more
   binding-form/collection-expr pairs, each followed by zero or more
   modifiers, and yields a lazy sequence of evaluations of expr.

micha16:12:50

interesting, they use "pairs" there

dm316:12:24

but it also lists the combinations

dm316:12:32

so binding-form/value-expr pairs?

micha16:12:53

yeah i'm writing the docstring for cell-doseq macro

candera16:12:16

“binding pairs”, IMO

micha16:12:16

yeah clojure doesn't reallyhave pairs so i guess it's clear what that means

dm316:12:11

@micha re what you wrote yesterday regarding components/applications - did you think of a way to specify elem "schema": their input cells, etc, so that they could be inspected/wired dynamically? Would you consider clojure.spec for this case?

micha16:12:51

yeah i think it would need something like spec for that

micha16:12:47

and with clojure.spec that part could be used only in dev perhaps

micha16:12:36

which would maybe avoid production performance issues related to higher level reflection like what we'd need to do

flyboarder17:12:19

Dynamic binding is working wonders for me in this area also