ClojureScript

A way to create js objects and arrays from cljs maps and vectors, without copying if possible.

Details

  • Type: Enhancement Enhancement
  • Status: Open Open
  • Priority: Minor Minor
  • Resolution: Unresolved
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None
  • 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.

Activity

Hide
David Nolen added a comment -

Waiting on whether Clojure becomes a JSON superset.

Show
David Nolen added a comment - Waiting on whether Clojure becomes a JSON superset.
Hide
Nicolas Buduroi added a comment -

Any update on this issue?

Show
Nicolas Buduroi added a comment - Any update on this issue?
Hide
Aaron Brooks added a comment -

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.

Show
Aaron Brooks added a comment - 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.
Hide
Chouser added a comment -

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).

Show
Chouser added a comment - 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).
Hide
David Nolen added a comment -
Show
David Nolen added a comment - Some work towards a solution: https://github.com/clojure/clojurescript/compare/37-support-for-js-literals
Hide
Chouser added a comment -

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?

Show
Chouser added a comment - 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?

People

  • Assignee:
    Unassigned
    Reporter:
    Chouser
Vote (4)
Watch (7)

Dates

  • Created:
    Updated: