Skip to content

Selecting member properties

Expressions such as a.b, or a["b"], or ('a' + 'b').length are called member expressions in JavaScript and are the product of using . or [] to access some object's property. The left-hand side of the expression, or the object, does not need to be an object literal, as is seen in the third example -- any expression that produces an object that is then accessed with either of these operators results in a member expression.

The (mem) selector, which stands for member, selects such expressions. It has the following signature:

clojure
(mem [prop?] [obj?])
(mem [prop?] [obj?])

In its bare form, (mem) selects any member expression, regardless of either property or object:

clojure
(mem) ; a.b
      ; a[b]
      ; a().b
      ; ('x' + 'y').b
(mem) ; a.b
      ; a[b]
      ; a().b
      ; ('x' + 'y').b

How useful that actually is, though, I'll leave to you. Luckily, we can do more!

Selecting by property

The first argument to (mem) selects the property being accessed; this is what's on the right-hand side, or the b in a.b.

clojure
(mem b)
(mem b)

Member properties are not restricted to identifiers; we've seen the computed form already, so to make it work for a[b] we can use the (comp) selector:

clojure
(mem (comp b))
(mem (comp b))

There is a third, more obscure form for member properties known as the private identifier. This looks similar to other identifiers only that it starts with a # (pound) sign. The (id) selector is able to match private identifiers when the name is prefixed by that pound sign:

clojure
(mem #b) ; a.#b
(mem #b) ; a.#b

Private identifiers are legal only in the context of classes in the JavaScript language but this restriction is not regarded in any special way by SYNG.

Selecting by object

The second argument to (mem) selects the object whose member is being accessed, or the a in a.b.

clojure
(mem b a)
(mem b a)

By now, the syntax and structure should be familiar to you. The arguments behave as you would expect in that we can use a logical operator to, say, alternate between properties:

clojure
(mem (:or b c) a) ; a.b
                  ; a.c
(mem (:or b c) a) ; a.b
                  ; a.c

And when the property is irrelevant, but the object or its access isn't, the placeholder atom can be used in its place to disregard it:

clojure
(mem _ a) ; a.b
          ; a.c
(mem _ a) ; a.b
          ; a.c

Recap

In the beginning of this section, we learned how to select identifiers and basic literals using (id), (str) and friends. Then we encountered array literals whose elements also had a positional component that we could select with (el). The set of logical operators - (:and), (:or) and (:not) - made it possible to build more powerful queries. Object literals were similar to arrays in many aspects, but now we're able to select access to properties of any kind of object using (mem), not just literals.

There's already a lot of utility in selecting each of those expressions, but the picture wouldn't be complete without the ability to select function calls, which can utilize all of what preceded.

Function calls are next: the pinnacle of SYNG's offering.

Copyright © 2022-present Semantic Works, Inc.