<< Back to previous view

[CLJS-37] A way to create js objects and arrays from cljs maps and vectors, without copying if possible. Created: 26/Jul/11  Updated: 18/Sep/12

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Chouser Assignee: Unassigned
Resolution: Unresolved Votes: 4
Labels: None

Attachments: Text File 0001-Add-as-js.-CLJS-37.patch    
Patch: Code and Test

 Description   

There's currently no convenient way to create a js object from a map. Arrays can be created using the `array` function, but this always creates a copy. In cases where you know the object or array will not be modified, it would be a shame to still pay for a copy of the object. This is especially true when the cljs map or vector is a literal such that nothing else can have a reference to it anyway.



 Comments   
Comment by Chouser [ 26/Jul/11 11:07 PM ]

My plan is a macro `as-js` that takes a single expression. If it's a literal vector or map, it will expand to a js* form that will emit the appropriate array or object literal. In the case of a map, all the keys must be constants (string, keyword, or symbol) in order to for this to work since literal object keys in javascript aren't evaluated. Keyword and symbol keys would be converted to strings.

If the argument to the `as-js` macro isn't a literal (or is a map with non-constant keys), the macro will expand to a call to an `-as-js` protocol fn. When called on a vector, the vector will return its internal array. When called on an ObjMap whose keys are all s convtrings, its internal strobj will be returned. If any of the ObjMap's keys are not strings, then no appropriate js obj exists and a new one will be created, converting keys to strings just like the macro above. HashMaps never contain an appropriate js object, so a new one would be created, again converting keys to strings.

So in the common case of maps whose keys are known at compile time, such as (as-js {:foo "bar" :baz (str "qu" "ux")}), this would at compile time become the js obj literal {"foo": "bar", "baz" cljs.core.str("qu", "ux")}. When the map or vector is not a literal, the least possible copying is done to produce the correct obj or array at runtime.

...why do I have this annoying feeling that I may have done something too compound here?

Comment by David Nolen [ 28/Oct/11 6:58 PM ]

Some work towards a solution: https://github.com/clojure/clojurescript/compare/37-support-for-js-literals

Comment by Chouser [ 28/Oct/11 8:53 PM ]

The macro-based solution I outlined above is unacceptable.

The key issue is that Rich wants the reader to actually create the target type (js array or object) at read time, so special reader syntax is required. I think the syntax was to be #js[1 2 3] for arrays and #js{k v k v} for objects. Then of course there needs to be support in the compiler to generate code that evaluates the array elements and the object values (but not their keys, since the js literal produced can't have expressions for keys).

Comment by Aaron Brooks [ 29/Oct/11 12:28 PM ]

Does a special reader syntax ask for something readable across other platforms? i.e. #native[]/#native{}

It seems that this would enhance the ability of ClojureScript libraries to be reusable across other platforms. It's easier to get around macro/function differences between platforms, however reader syntax differences would cut off the options for sharing source files between Clojure platforms.

Comment by Nicolas Buduroi [ 11/Apr/12 12:30 AM ]

Any update on this issue?

Comment by David Nolen [ 11/Apr/12 2:41 PM ]

Waiting on whether Clojure becomes a JSON superset.

Generated at Sat Jul 26 10:27:05 CDT 2014 using JIRA 4.4#649-r158309.