Destructuring Clojure with Javascript

Clojure is a language I am learning as I work on awesome stuff at Swym Corporation. When I came on board last year, I had no clue what it meant unless it was the thing that causes problems in Javascript for loops. Turns out it is pretty close to Javascript.

Coming to Clojure. It is a beautiful language. Weird at first, but it grows on you. Functional languages somehow tend to be. In this post, I am going to talk about a feature shared by many languages, but with specific details in Clojure and Javascript with their joys and pains. Destructuring. Why Clojure??—?that’s a post for another time.

Destructuring makes assignments easier, which could instead take multiple lines of code.

Simple destructuring,

To put it simply, load variables from vectors and arrays quickly without repeating a word more or less.

//JS// Old way
?var list = [1,2,3];
var x = list[0], y = list[1], z = list[2];
console.log(x, y, z);
// Destructured way
?varlist = [1,2,3];
var [x, y, z, a] = list;
console.log(x, y, z, a);
// Note - Any extra parameter not accounted for becomes undefined

One stark between Clojure and JS, is the immutatablity of data-structures. Like variable assignment is available, but not necessarily good to use. This plays heavily when understanding Clojure, I thought of it as a handicap initially. But as time went by, the intuitiveness showed itself.

;; Clojure
;; Using let definition, same can be done with a def for the list
(let [list [123] [x y z] list] (println x y z))
;; Better one, list ref created within the destructuring
(let [[x y z a :as list] [123]] (println x y z a list))
;; Note - Extra parameter unaccounted for becomes nil

Going a step deeper into vectors, especially useful when you don’t want to list out arguments and destructure as they come

// JS
?// Bunching rest of the values with a spread operator
?var list = [1, 2, 3, 5, 6, 7, 8];
var [x, y, z, …a] = list;
console.log(x, y, z, a);
// Note - The last param will be the spread operator(...)
?// Powerful for array merging, and data processing. More on spread operators// Ignoring partsvarlist = [1, 2, "ignore me", 3];
var [x,y,,z] = list;
console.log(x,y,z);

Clojure matches with it

;; Clojure
;; Spreadoperator
(let [[x y z & rest-of-it :as list] [1235678]]
 (println x y z rest-of-it))
;; Ignoring 
(let [[x y _ z :as list] [12"ignore me"3]]
  (println x y z))

Picking default values is simple, without all those flimsy if checks from yore.

// JS// Old way - Default values
?var list = [1,2];var x = list[0], y = list[1];
var z = list[2] || 'default z'; // This checkis flimsy, you need tocheckfor undefined to avoid falsy errors. for eg: false will pass tosecond part// Default valuesvarlist = [1, 2];var [x,y,z="default z"] = list;
console.log(x,y,z);

Same in Clojure

;; Clojure
;; Default values
(let [[x,y,z :or [z 10]] [1 2]]
 (println x y z))

Objects and nested structures

This is where the real fun lies. Very powerful for functions which take configurations and optional parameters by simplifying the assignments.

// JS// Object
?var o = {“x”: 1, “y”: 2, “z”: 3};var {x, y, z} = o;
console.log(x, y, z);
// Also works - var {x, y, z} = {x: 1, y: 2, z: 3};// Also works - var x,y,z; ({x, y, z} = o);// Renamingvar o = {“x”: 1, “y”: 2, “z”: 3};var {x: a, y: b, z: c} = o;
console.log(a, b, c);
// Default values with renamingvar o = {“x”: 1, “y”: 2, “z”: 3};var {x, y, z: c, p: d=10, q=20} = o;
console.log(a, b, c, d, q);
// Usage in functionsfunctiondoSomethingAwesome({mandatoryParam, renameParam: changedParam, optionalParam=1, optionalRenameParam: changedOptionalParam=2}){console.log(mandatoryParam, changedParam, optionalParam, changedOptionalParam);
}
doSomethingAwesome({mandatoryParam: 10, renameParam: 20});
// Nested objectsvar o = {
 “a”: {
 “x”: 1,
 “y”: [2, 3]
 }
};
// With renaming and default valuesvar {a: {x, y: [aa, bb, cc=20], z=10}} = o;console.log(a, x, y, p, aa, bb, cc, z);

Compared to Javascript, Clojure has some guns, like :keys and :strs binding forms to bunch the destructuring together. Warning?—?Entering a little more complicated territory

;; Clojure
;; Object — not so efficient when there is no renaming
(let [{x :x y :y} {:x1:y2:z3}]
 (println x y))
 
;; Object with renaming
(let [{rx :x y :y} {:x1:y2:z3}]
 (println rx y))
;; Object with keywords and strings, but no renaming
(let [{:keys [x y z] :as list} {:x1:y2:z3}
 {:strs [xx yy zz] :as list-str} {“xx” 11 “yy” 22 “zz” 33}]
 (println x y z xx yy zz))
;; Object with defaults
(let [{:keys [x y z xx yy zz] :or {xx 10 yy 20 zz 30} :as list} {:x1:y2:z3}]
 (println x y z xx yy zz))
 
;; Object defaults with renaming
(let [{rx :x y :y rz :z:or {rz 30}} {:x1:y2}]
 (println rx y rz))
 
;; Usage in functions — not so efficient
(defn do-something-awesome [{
 mandatoryParam :mandatoryParam
 changedParam :renameParam
 optionalParam :optionalParam
 changedOptionalParam :optionalRenameParam:or {optionalParam 1 changedOptionalParam 2} }]
 (println mandatoryParam changedParam optionalParam changedOptionalParam))
(do-something-awesome {:mandatoryParam10:renameParam20})
;; Usage in functions — better
(defn do-something-awesome [{
 :keys [mandatoryParam optionalParam optionalRenameParam]
 changedParam :renameParam
 changedOptionalParam :optionalRenameParam:or {optionalParam 1 changedOptionalParam 2}
 }]
 (println mandatoryParam changedParam optionalParam changedOptionalParam))
(do-something-awesome {:mandatoryParam10:renameParam20})
;; Nested objects
(defo {:a {
 :x1:y [2, 3]
 }
})
;; With default values
(let [{ {:keys [x y]} :a} o
 [y1,y2] y] ;; forced to add a line to break y to params
 (println x y y1 y2))
;; y1, y2 don’t get assigned, what’s wrong? a TODO for me to figure out
(let [{ {:keys [x [[y1,y2] :y]]} :a} o]
 (println x y y1 y2))

The usage of :keys binding form and regular destructure is a good way to use rather than only one. Renaming is supported with :as binding form at a higher level

Now time for some overkill. Lets see which combination of the above breaks the REPL.

JS Overkill?—?Very hard to find this case?—?renaming with internal destructuring

// Overkill?
?var o = {
 “a”: {
 “x”: 1,
 “y”: [2, 3]
 }
};
var {a: {x, y: p = [aa, bb, dd=20], z=10}} = o;
console.log(a, x, y, p, aa, bb, dd, z);
// Throws error, renaming with internal destructuring doesn’t work

Clojure Overkill?—?Was far easier to find. Trying to destructure a vector inside an object while destructuring the object.

;; y1, y2 don't get assigned, what’s wrong? a TODO for me to figure out
(let [{ {:keys [x [[y1,y2] :y]]} :a} o]
 (println x y y1 y2))

Destructuring makes for a more intuitive code and easier to write functions, read functional programming. Comparing the two, it makes Javascript’s functional origins clearer. A new point to wonder —Does the Javascript prototype object fall under a functional programming paradigm?

Unfortunately for Javascript users, destructuring is not fully safe to use on all browser based JITs, but if you know your deployment, go for it.

Do let me know your thoughts and how you would use them, and please improve anything I may have written wrong.

References

Javascript

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

Clojure

Very useful to read this one first?—?https://blog.brunobonacci.com/2014/11/16/clojure-complete-guide-to-destructuring/

https://clojure.org/guides/destructuring

https://gist.github.com/john2x/e1dca953548bfdfb9844

P.S: PHP sucks, Javascript is beautiful, Java is consistent, CSS is beautiful again, Objective C is weird, Python is sensitive(read whitespace), C#/.Net is a mutant and so on. All personal opinions.

This post was initially featured at https://medium.com/@aravindbaskaran/destructuring-clojure-with-javascript-bd1398bdacb6



要查看或添加评论,请登录

Aravind Baskaran的更多文章

  • Fullmetal Alchemist and the philosophy of Debugging

    Fullmetal Alchemist and the philosophy of Debugging

    Finally got through all the content around the much-hyped Fullmetal Alchemist in 2020, and I was not disappointed;…

  • Playback Theatre and the Startup mindset

    Playback Theatre and the Startup mindset

    I have always been fascinated with the theatre and aspired to "learn" theatre (still do). Between 2017 - 2019, I was…

    2 条评论
  • State of Cycling in Bengaluru 2020 ??

    State of Cycling in Bengaluru 2020 ??

    My opinions, albeit with a clickbait This past week I had to cab it. My cycle is on a break, getting a full workup done.

  • Cycling in Bengaluru?—?Lessons and challenges ??

    Cycling in Bengaluru?—?Lessons and challenges ??

    Recounting the challenges and lessons from 3 years (and counting) of cycling in probably a not-so-cycle-friendly “big…

    4 条评论
  • Clojure -Lessons from?Laziness

    Clojure -Lessons from?Laziness

    Laziness can be a good thing. Maybe the best of things? Surely at the backend of computations, being lazy is mighty…

    1 条评论
  • Racist Security with Clojure

    Racist Security with Clojure

    Many of you may have landed here based on the first half of the title, maybe the title is a tad misleading. Sorry about…

  • A series of mistakes

    A series of mistakes

    Inertia. The only thing momentum is afraid of.

    1 条评论

社区洞察

其他会员也浏览了