Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prop->key and key->prop for values #67

Closed
ingesolvoll opened this issue Jul 3, 2019 · 6 comments · Fixed by #78
Closed

prop->key and key->prop for values #67

ingesolvoll opened this issue Jul 3, 2019 · 6 comments · Fixed by #78

Comments

@ingesolvoll
Copy link

ingesolvoll commented Jul 3, 2019

In the app I'm working on we use custom converters to be able to preserve namespaced keywords through a cljs->js->cljs roundtrip. In cljs-bean this fits perfectly, we were able to reuse our code by plugging it in as prop->key and key->prop converters.

That's for the keys, which is obviously the most common and sensible use case. But we also occasionally need to do the same for property values. So for us it would be pretty great to be able to extend the cond statement in the ->val function.

@mfikes
Copy link
Owner

mfikes commented Jul 5, 2019

Cool! I have a few questions.

With respect to key conversion, consider the following, which illustrates preservation of namespaces keywords:

cljs.user=> (require '[cljs-bean.core :refer [->clj ->js]])
nil
cljs.user=> {:my-ns/foo 1 :my-other-ns/bar 2}
{:my-ns/foo 1, :my-other-ns/bar 2}
cljs.user=> (->js *1)
#js {"my-ns/foo" 1, "my-other-ns/bar" 2}
cljs.user=> (->clj *1)
{:my-ns/foo 1, :my-other-ns/bar 2}
  1. Does your key->prop and prop->key behave differently than the above illustrates? (No need to provide exact details; I'm just curious if it differed.)
  2. Did you find a need to pass these into either ->clj or ->js? I ask because only bean lets you do this currently. Perhaps you are using bean and passing your own converters and also passing :recursive true, and not even using ->clj or ->js?

With respect to value conversion, it sounds like your use case might involve objects that don't naturally have a representation in JavaScript, or maybe you desire to control that representation.

An example of that might be the following where you might not want a date instance appearing in your JavaScript object, but perhaps you might want to express it as a string.

cljs.user=> (->js {:foo/bar #inst "2019"})
#js {"foo/bar" #inst "2019-01-01T00:00:00.000-00:00"}

This seems to get into areas that even ClojureScript's js->clj and clj->js don't currently offer any control, right, and where Transit might offer the needed control? (Happy to consider ideas that might make the capabilities in CLJS Bean more useful here, but just trying to more clearly define the problem to be solved.)

@ingesolvoll
Copy link
Author

Thank you for the detailed feedback!

My key->prop and prop->key do the very same thing I just discovered cljs-bean does by default. So no need for that anymore, deleted them now!

I really like that cljs-bean has this as default behaviour. And it makes sense, when you know that your prop key is supposed to be a (potentially) namespaced keyword, it is natural and simple to encode and decode as a string.

With property values it's different because you don't know that your value string is supposed to be a keyword. For things like date instances and keywords you would need to parse every single value with custom predicates to look for patterns that would trigger custom decodings. Probably expensive, and also introducing some API complexity. And as you're saying, other libs does this well and are probably a better tool for the job.

@mfikes
Copy link
Owner

mfikes commented Jul 5, 2019

This project started off with the simple idea of "converting" js->clj by implementing thin wrappers supporting protocols like ILookup.

The ability to convert back from clj->js was motivated by a suggestion of Will Acton's in #10, which essentially resulted in CoW wherever feasible, while maintaining an internal JavaScript representation, and converting to a conventional persistent data structure when not.

So ->js is currently no more than the idea of "unwrap and return the internal JavaScript (in constant time) if possible, and if not, delegate to clj->js."

So, given this, it might be fair to say that CLJS Bean might be good at problems that start from JavaScript, such as js->clj->js. But your example of clj->js->clj, as discussed above, raises additional concerns surrounding control over value conversion.

Not adding anything new really by saying the above, just trying to clarify my thinking by spelling it all out.

I think I'm still of the opinion that fine-grained control of clj->js, especially in areas beyond what currently controllable in clj->js might be outside of the scope of this project, and better handled by things like Transit (especially if your problem is clj->js->clj).

But, then again the high-level "stance" of this project is to try to make it easy to relatively efficiently interoperate with JavaScript without causing you to need to learn new APIs (by essentially allowing you to use all of the usual functions like assoc, get, nth, and so on). In that spirit, you might find yourself needing to craft a JavaScript object, wanting to use the facilities in this library, building up said object using assoc etc, but then somehow wanting to have fine grained control over what happens when things get converted.

It is not currently clear to me how to do this, apart from your suggestion alluding to some sort of API that calls back with the key along with the value, so that you have all the hooks you need to control value conversion.

Maybe this ticket is worth sitting on until it becomes clearer which, if any route should be taken.

@mfikes
Copy link
Owner

mfikes commented Sep 8, 2019

This branch has an experimental change that allows clients to pass a function that will convert JavaScript values to ClojureScript values: https://github.com/mfikes/cljs-bean/tree/val-hook

@mfikes
Copy link
Owner

mfikes commented Sep 9, 2019

Alternative experiment is in https://github.com/mfikes/cljs-bean/tree/val-hook-2.

The primary difference is that, instead of user-supplied ->val user only supplies a transform fn that is used inside of ->val.

@darwin
Copy link

darwin commented Feb 26, 2020

@mfikes It would be great for me if val-hook-2 could get into next release. I started using it:
lilactown/helix#9 (comment)

No hurry. I can consume this lib via deps.end using git sha.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants