Fork me on GitHub

can dynamic resolvers reduce the work done in a resolver? e.g. if you have a resolver with ::pc/output [:first-name :last-name] and you only query for [:first-name], can pathom modify the resolver logic so it doesn't query the db for :last-name too?


@kevin842 you mean a way to check in the resolver what query was done to him, to maybe avoid providing parts of it?


yes, that's something dynamic resolvers can do, and what you said is how I imagine the implementation of things like SQL drivers could be


the new planner gives to the dynamic resolver the exact query it should fulfil


(based on the user request)


sounds perfect :) I'm using pathom with crux and I guess that means there's no cost to defining a single resolver for a big document, so pathom can describe a de facto schema for crux with a 1:1 mapping between resolvers and documents


Pathom 3 sounds great. You did say 'great time to bring up new ideas'. So the idea, which I know I've mentioned before, is for mutations to have inputs, so they work more like resolvers. The usual work around for this is to call the parser that's in the env again. Having inputs as 'first class' for mutations would make mutations look better, and surely improve other things too, like performance??


hello, I think this should be done in plugin lang, or using transforms, that's because adding the processing of parsing params as inputs can never be faster than just providing raw params, so I consider that mutations need to always have the most lightweight version possible available


but sure, I think providing those as built-ins could be a easy way to make it accessible, or maybe I'm missing something, what I said makes sense to you?


Yes pretty much. A second arg to the mutation. Usually the mutation would have only one arg as now. So the performance stuff can perhaps be done automatically from what the user indicates (one or two args)...


Smart maps are intriguing. Seems a bit like the concept of a lazy seq, applied to a map.


+1 on the smart maps. I always thought Plumatic’s Graph was a super cool, but it doesn’t work with namespaced keywords, etc. seems a super cool fit with pathom


yup, I feel like you guys, and I also believe this is a much easier way to use pathom for newcomers, so they don't have to learn anything about EQL but can still leverage the resolvers engine

👍 3

That’s a solid point

Chris O’Donnell15:09:05

Smart maps seem pretty neat, but I think I would prefer to use it via a different API from the normal clojure.core get. When I'm reading code and I see (:my.ns/foo m), I have an expectation that the operation will happen immediately. If (:my.ns/foo m) were to cause the parser to go out and make an API call to a microservice to get data (for example), I think that starts to tread on the principle of least surprise. Something like ( m :my.ns/foo) is a bit more verbose, but also more explicit.

☝️ 6
Chris O’Donnell15:09:35

Just 2 cents from someone with far less pathom experience. :)


@codonnell makes total sense, I think this decision between transparent or not can very depending on the use case. for example, I imagine smart maps can be used to make adapter layers, so you can swap names, in this case a user may prefer to have it transparent as a map, and don't care if its immediate or lazy. but if you are dealing with heavy things like API calls, and the surprise effect is problematic, then would be nice to make it explicit. and I think Smart Maps should support both cases, and this can be a matter of how to configure the smart map (to make lazy calls implicit or explicit)

💯 6

another interesting configuration that will happen is how the smart maps should respond to (keys smart-map), so far I imagine two options here: 1. cached - this is the current way, keys will respond with the keys that are cached on the map, so the lookup is constant and predictable 2. reachable - this will respond with all possible keys (considering the index and current context), interesting in some cases but needs to be used with care, because a (into {} smart-map) could trigger a lot of resolvers

Chris O’Donnell15:09:33

Oh yeah, that is an interesting wrinkle.

Chris O’Donnell15:09:42

2. could have interesting interactions with quite a bit of the clojure sequence API

Chris O’Donnell15:09:52

If (keys smart-map) returned something different from the tuples in (seq smart-map), for example, (into {} smart-map) could return something different from (reduce (fn [m k] (assoc m k (get smart-map k))) {} (keys smart-map)), which would be very surprising.

Chris O’Donnell15:09:09

Now that I'm thinking more about it, I can see how it would be valuable to treat smart maps like maps so you could use clojure.core collection functions on them.

Chris O’Donnell15:09:34

Don't want to come off to negative; I am pretty excited to see pathom 3 as it develops. 🙂

simple_smile 3

Keeping in mind the goal of being approachable by new-comers, I think the path of least surprise would be the most sensible default, which to my mind would be get and keys behaving as you would expect them to behave when applied to a map. I.e., return the value, return all keys (does the map necessarily have to realise the values to return the complete set of keys?).


@henrik I agree on the path of least surprise, to me so far that is having keys listing only cached, and with transparent reads


the keys thing doesn't need to realize the value, but gets dangerous when doing any scan operation (like (into {} smart-map))


I’d have to disagree with that, since it kind of becomes mutable (and therefore surprising). Reading the map changes the output of keys.


we could have different results from keys and seq, but I'm not sure that is least surprising, I guess we need to experiment more to figure those things out


Well, scan operations being potentially expensive isn’t unheard of in Clojure. Any lazy construct will have pitfalls like that.


yeah, but I fear that it could hurt programs in quite bad ways, therefore I rather have the default to be safer in this case


I think the “mutability” is the more dangerous problem in this case. We all have REPLs, and we can evaluate something and gauge the performance of it. But when you start talking about something that “changes in place”, like the output of keys following a get, you can easily get the wrong impressions of what’s happening just because you evaluated your forms in a particular order while developing (as in oops, you evaluated a get, the keys output is not going to look the same in production as it did in your REPL).


I can understand that, but I still more afraid of long runs, IME the graphs tend to get really big, and when they do, a single attribute may reach hundreds of resolvers, so even on the REPL a simple scan on a single attr could stop and break everything, this makes me feel this is a too dangerous operation to be the default, but as I said, from impressions so far, I'm down to change opinion depending on how the use cases unfold and we have a better understanding of the most common types of usage


In that case, maybe it would be better to stay away from core entirely, since it would kind of redefine what those functions do anyway, and expose a custom set of functions to interact with them.


With a disclaimer that I’m not at all sure, it’s certainly a tricky bunch of tradeoffs to juggle.


will really depend on each use case, I really like the transparent idea for conversion layers, and users that think better defaults should be different, you can write your own smart map constructs to change the defaults in your application context, do you think that's a reasonable solution for difference in opinions around the defaults?


oh yeah, I agree its a tricky jungle, and I hope we can figure it out together what best practices around it may be (or even if its just a really bad ideia, haha)


If “build your own” means just seeding some kind of reusable construction function with a config that flips a few switches, maybe. Otherwise, people are probably going to use the defaults or avoid it entirely and go “classic”.


yeah, I mean writing something like this:

(defn my-smart-map [env context]
    (merge {::psm/keys-mode ::psm/keys-mode-reachable} 

👍 3

If you expect it to be common enough, you could shave away the env, context with a helper maybe:

(def my-smart-map (psm/make-smart-map {::psm/keys-mode ::psm/keys-mode-reachable}))


we can use a partial in that:

(def my-smart-map (partial psm/smart-map {::psm/keys-mode ::psm/keys-mode-reachable}))
what makes hard to shave the env out is because you also need to provide indexes, if your app wants to use a single index for all smart maps, that's fine, but otherwise you will still want to have the env available to change (to use different indexes)


and maybe different indexes will be the key for different usages, a simple index with mostly aliases and quick operations should be safe to allow all keys, but one with many heavy resolvers will want a different setting


(defn make-smart-map [config]
  (fn [env context]
    (psm/smart-map (merge config env) context)))


What if the smart-map followed the conventions of Datomic Peer entities? keys would only return cached keys and you would have to do something akin to d/touch to realize everything.


@pithyless this is the direction I'm inclined to, sm also support (psm/load! smart-map eql-to-touch), which is similar the touch you are talking (but more specific, not getting everything, altough that could be an option)


Yeah, that sounds great. I'm all in favor of being explicit via EQL ;]


One could always support (psm/load! smart-map '[*]) if that was a common feature request.

👍 3