JavaScript Interop

HQL compiles to JavaScript and can call JavaScript values directly.

Property Access

(let user {name: "Ada", age: 37})
user.name
user.age

Dot syntax compiles to JavaScript property access.

Constructors

(let now (new js/Date))
(js-call now "getFullYear")

js/Name resolves a JavaScript global.

Method Calls

(let items [1, 2])
(.push items 3)
items.length

Use method-call syntax for calls that need a receiver.

General Calls

(js-call JSON "parse" "{\"ok\":true}")
(js-call JSON "stringify" {ok: true})

js-call calls a property by name on a target object.

Data Lane Matters

The reader rule controls whether a literal is persistent HQL data or native JS data:

[1 2 3]            ; persistent vector
[1, 2, 3]          ; JS array
{:name "Ada"}      ; persistent map
{name: "Ada"}      ; JS object

Use to-js and from-js when crossing data boundaries explicitly.

to-js is for sending persistent HQL data to JavaScript APIs:

(let user {:name "Ada" :scores [10 20]})

(js-call JSON "stringify" (to-js user))
; {"name":"Ada","scores":[10,20]}

from-js is for bringing JavaScript data back into HQL collection operations:

(let user (from-js {name: "Ada", scores: [10, 20]}))

[(persistent-map? user)
 (get user "name")
 (to-js (get user "scores"))]
; [true, "Ada", [10, 20]]

The conversion is recursive for arrays, plain objects, Maps, and Sets:

(let data (from-js {user: {name: "Ada"}, tags: ["hql", "js"]}))

(get-in data ["user" "name"])
; "Ada"

Persistent maps with string or keyword keys become plain JavaScript objects. Persistent maps with other key types become JavaScript Map values:

(let by-id (hash-map 1 "Ada"))

[(instanceof (to-js by-id) js/Map)
 (js-call (to-js by-id) "get" 1)]
; [true, "Ada"]

Persistent sets become JavaScript Set values:

(let tags (from-js #["red", "blue"]))

[(persistent-set? tags)
 (instanceof (to-js tags) js/Set)
 (js-call (to-js tags) "has" "red")]
; [true, true, true]

Error Handling

JavaScript exceptions can be caught with HQL error handling:

(let input "{bad")

(try
  (js-call JSON "parse" input)
  (catch err
    {:error (str err)}))

Next