This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-09-26
Channels
- # announcements (2)
- # asami (2)
- # aws (34)
- # babashka (6)
- # beginners (9)
- # calva (76)
- # chlorine-clover (10)
- # circleci (5)
- # clj-kondo (2)
- # clojure (40)
- # clojure-australia (3)
- # clojure-europe (15)
- # clojurescript (39)
- # conjure (1)
- # core-async (4)
- # cursive (4)
- # datahike (1)
- # datomic (69)
- # figwheel-main (1)
- # graalvm (16)
- # honeysql (9)
- # hyperfiddle (2)
- # jobs-discuss (2)
- # lsp (36)
- # luminus (1)
- # malli (11)
- # off-topic (13)
- # pathom (1)
- # portal (1)
- # portkey (3)
- # reitit (25)
- # reveal (1)
- # rewrite-clj (5)
- # spacemacs (2)
- # sql (4)
- # vrac (90)
In Vrac, the implementation will also use "canonical paths" which are paths without any entity reference inside. It's basically a path which you could use in (get-in app-db path)
so if you have a model where the same logical entity might show up in multiple places, is it the db's job to make sure updates to a logical entity get propagated?
that's more or less what I've been thinking, but I haven't found a db with the right API
By "same logical entity in multiple place", you mean multiple copies of the same data, or multiple references to the same data?
I probably phrased it poorly. The UI typically works based off the denormalized data from a query and it's probably stored in a normalized form
I responded before I read your #2 from above. I think follow. Is it correct to say that a schema for the data model will be required so that you can tell whether a reference is an entity reference?
I am thinking of 2 different ways to make it work: 1. Make the user list all the keys which will always point to entities, a.k.a. "tagged relations" 2. Use a special data type for representing a reference to an entity. <-- that the approach which I am using in my PoC.
The solution 2. avoids having to specify a schema manually.
https://github.com/green-coder/vrac-simple-sample/blob/master/src/vrac/reframe.cljc#L8-L9
oh wait ... I am not sure if I did it that way ... I kind of forgot already.
Yes, that's how I made it: https://github.com/green-coder/vrac-simple-sample/blob/master/src/vrac/reframe.cljc#L55
The Id
type acts as an id and a reference at the same time, depending on where it is used.
right, but it doesn't seem like it would uncommon to need to refer to a property of an entity
like if you have a checkbox, it could be toggling the :todo/complete?
property of a todo item and you would need a reference to that property for the checkbox
It would be referenced via its path in the db, something like [:entities (Id. xxx) :todo/complete?]
oh, so the component would at some point need to do (get some-entity (Id. entity-id))
Not explicitly (not visible for the user), but the implementation, yes.
> Not explicitly (not visible for the user), but the implementation, yes. I'm not sure I follow
I just mean "yes" 🙂
yea, if you don't have entities, the key-path works great. It's still unclear to me what the best approach is for handling a data model with entities: • require a schema • components required to know which keys are references • try to support both • something else
In my case, I have entities, no schema, and a special data type to represent references.
isn't that a form of "components required to know which keys are references"
not that it's a bad thing
just trying to see what trade-off others are trying and how they feel
> isn't that a form of "components required to know which keys are references" It's not. The component doesn't know anything. The only thing he knows is to follow a path.
so where does the special reference type get inserted?
They are not in the path. They are in the data pointed by the path.
so it's in the db?
Yes, and it's totally dynamic
what's the difference between that and having a schema?
schemas are static vs. data driven is dynamic
For the user, no schema might mean less work.
Both approach can be supported by the framework, I think.
I could see that working for an existing reference
how would a new reference be added?
The only part which would need to change would be the function which follows a path to retrieve some data.
for example, you have list of todo-items and each todo item is a reference. when you add a new todo, it's the "effect handler"'s job to create the reference?
> how would a new reference be added?
with-id
is a function which turns an hashmap into an entity.
https://github.com/green-coder/vrac-simple-sample/blob/master/src/vrac/reframe.cljc#L8-L9
from that entity, you can get a reference to it using (:vrac.db/id my-entity)
the main issue is illustrated when you have a dynamic component like a tabbed pane. The selected tab is part of the data and you don't know what data you need to show until you've already queried a subset of the data (ie. each tab will have different data and you don't know which data will be needed for the current render until you know which tab is selected)
so all the effect handlers would need to know which keys are references?
The effect handlers contains the logic of how to change the app-db. When we are not using a schema, the code in the handler also have to know when to use a reference instead of just the data.
I'm still not sure which options I like best. One thing I've noticed is that it's really nice to have an entity-less data model for testing components in isolation, but you usually want an actual db when integrating a full UI. Having generic effect handlers can help.
There's also generic components like date* pickers, and list views that ideally shouldn't care
Reusable & generic components like a date picker or would normally not dispatch their own events, they would dispatch events provided by their parent.
well, the event system in membrane works different than what you're probably used to and no one is dispatching events, but generic components like date pickers do have default handlers
in that case, you can have a schema where the user provides a list of qualified keywords which will always refer to entities.
But that won't solve all your problems, if you default handler have to dissoc
data and does not know if the data is just fields in an entity or the entity itself.
why wouldn't the schema handle that?
When working on Clojure values, you can just dissoc
. On a db it's different, you need to say if you delete a link to an entity or if you also want to delete the entity.
The schema won't answer this question. If the code in the default handler of your date picker answer this question, it means it is aware of where the entities are.
One solution would be that it answers that question, regardless of the kind of data it deals with. In other word, it would say something like dissoc
or dissoc-delete
for the operations to be done on the data.
right, if you write* the code using that operation, it would work in either context
in the non-entity db context, it would just be a dissoc
Now I remember that I run into that in April and I forgot.
https://github.com/green-coder/vrac-simple-sample/blob/master/src/vrac/reframe.cljc#L170-L176
> For example, you have list of todo-items and each todo item is a reference. when you add a new todo, it's the "effect handler"'s job to create the reference? Yes
I might add support for a schema (i.e. the user lists the keyword relations pointing to entities) in Vrac, for the users who need it.
@smith.adriane inside:
(defui counter [{:keys [num]}]
(horizontal-layout
(on :mouse-down (fn [[mouse-x mouse-y]]
[[::counter-increment $num]])
(ui/button "more!"))
(ui/label num)))
If I understand correctly, the view passes the reference $num
to the event. Does it mean that the view has to know when to pass the reference vs. pass the value?it passes both, the defui
macro takes care of passing the extra reference info
it's a bit of an implementation detail, but every function defined using defui
has a special key in it's meta data. the defui
macro will look at every function call within the body and automatically include the extra info when calling a function that has the special meta data
and to pass the extra info, every defui
is required to except a single map as its argument
It was initially meant to be a temporary experiment, but it seems to work surprisingly well in practice
What I mean is: does the user sometime passes num
and sometimes passes $num
?
you mean when calling counter
or for effects?
when calling counter
, the user will only ever pass num
and $num
will be passed implicitly
when creating the effect
for effects, it will depend
In Vrac, the user would types num
all the time, it means passing the reference. The effect handler would be the one to know if it needs the reference or the value.
you can always get the value from the reference, but not always the other way around
It means that the view does not have to know how the effect handler works
Yes, exactly.
that's something I would have to think more about. The effect is supposed to reflect the intent of the user. I'm not sure whether describing the intent of the user should differentiate between a reference or the value
and some arguments are clearly not values references
that's an interesting point though
when you implement an effect, do you say which args are values vs refs? can you get both?
for an intent like [:toggle $bool]
, it doesn't make sense unless $bool
is a reference.
for an intent like [:add-to-counter $counter 42]
, the argument 42
is a literal and no reference exists. I'm trying to think of an example where the difference between a reference or a value would be an implementation detail.
it's also getting a little late here, so my 🧠 might not be working 100%
I have the same goal of trying to deduct which one to use, based on how the effect handler is implemented. I am not sure if it is always possible to deduct it. In the worst case, the user can explicitely write something like (ref counter)
when he wants to use the reference inside the effect handler.
That's an area which will need clarification in Vrac.
I have this in my PoC: https://github.com/green-coder/vrac-simple-sample/blob/master/src/simple/vrac_demo.cljc#L40-L42
In this simple case, color
is a reference and new-color
... it depends what it is, it could be a ref or a value.
new-color
is a value, I think
I guess the question I was trying to ask is if the reference/value difference should be specified by the effect itself and not the implementation. The benefit of specifying it as part of the effect is that you can catch errors earlier
since passing a value when a ref is required is an error
additionally, I like the idea of being able to provide different implementations for effects depending on the context (eg. testing, debugging, different uses cases, etc)
so having ref vs value as part of the effect definition might be helpful (or hurtful)
> since passing a value when a ref is required is an error but passing a ref all the time means no error 🙂
but you can't: [:add-to-counter $counter 42]
. 42 is not referencable
ah, you are right