Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Two Ways to Access Properties in ClojureScript

DZone's Guide to

Two Ways to Access Properties in ClojureScript

· Web Dev Zone
Free Resource

Never build auth again! Okta makes it simple to implement authentication, authorization, MFA and more in minutes. Try the free developer API today! 

There are two pairs of complementary functions to set properties on objects in ClojureScript. One is aset and aget, another is set! and .-propname:

(def scope (js-obj))
(aset scope "var1" "Value")
(aget scope "var1")
(def scope (js-obj))
(set! (.-var2 scope) "Value")
(.-var2 scope)

Are they equivalent? Is syntax the only difference?

For a demo, let’s say we set and print two fields on an object, like this:

(def scope (js-obj))
(aset scope "var1" "Value")
(set! (.-var2 scope) "Value")
 
(.log js/console (str "(aget scope \"var1\") ->" (aget scope "var1")))
(.log js/console (str "(.-var2 scope) ->" (.-var2 scope)))
(.log js/console (str "(.-var1 scope) ->" (.-var1 scope)))
(.log js/console (str "(aget scope \"var2\") ->" (aget scope "var2")))

The resulting “setting” instructions are deceptively similar:

mynamespace.scope = {};
mynamespace.scope["var1"] = "Value";
mynamespace.scope.var2 = "Value";

And of course all combinations print out correctly – I can see this on the console:

(aget scope "var1") ->Value
(.-var2 scope) ->Value
(.-var1 scope) ->Value
(aget scope "var2") ->Value

So far so good.

Now, if you’re at least a little bit serious about using ClojureScript, you have to consider advanced optimizations. What happens in this mode?

My code got compiled to:

var Xm;
Xm = {var1:"Value", lc:"Value"};

And on the console all I see is:

(aget scope "var1") ->Value
(.-var2 scope) ->Value
(.-var1 scope) ->
(aget scope "var2") ->

What happened to var2? Why is set! suddenly incompatible with aget and aset with .-field?

If you look at compiler output in both cases, you should see that in one case the value is bound by name and in the other by symbol. In my understanding, it has a few consequences:

  • Advanced compiler renames symbols, but leaves strings intact.
  • aset and aget operate on names. set! and .-var operate on symbols. If you set a variable using “symbolic reference”, the compiler will rename the symbol so you won’t be able to get it by name anymore. If you set it by name with aset, reading by symbol as .-var will not work either because the symbol is renamed.
  • If you deal only with ClojureScript, you should probably use the symbolic references. They look more idiomatic and will benefit from advanced compiler features such as minimization. On the other hand, in this case you often may want to leverage the idiomatic data bindings – vars, atoms and such.
  • As soon as you want your names to be accessible from outside, or you want to access names set outside, use onlyaset/aget. It obviously applies to interop with JavaScript libraries. Less obvious case (for me) was libraries that operate on DOM, like Angular or Knockout. If you want that {{user.name}} to work, you cannot use the symbolic version.

It all makes sense now, but I haven’t seen it documented. Well, that’s still true for most ClojureScript.



Launch your application faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:

Published at DZone with permission of Konrad Garus, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}