;; In response to blog post: | |
;; https://medium.com/@kasperpeulen/10-features-from-various-modern-languages-that-i-would-like-to-see-in-any-programming-language-f2a4a8ee6727 | |
;; Run with lumo | |
;; https://github.com/anmonteiro/lumo | |
;; | |
;; # npm install -g lumo-cljs | |
;; lumo clojurescript-feature-examples.cljs | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "Example #1 - Pipeline Operator") | |
;;let result = "hello" | |
;; |> doubleSay | |
;; |> capitalize | |
;; |> exclaim; | |
;; Equivalent in clojure would be threading macros | |
(defn double-say [x] (str x ", " x)) | |
;; Grab clojure.string/capitalize | |
(require '[clojure.string :refer [capitalize]]) | |
(defn exclaim [x] (str x "!")) | |
(def result (-> "hello" double-say capitalize exclaim)) | |
(println "Result:" result) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "\nExample 2 - Pattern Matching") | |
;;let reply = | |
;; switch message { | |
;; | "Reason's pretty cool" => "Yep" | |
;; | "good night" => "See ya!" | |
;; | "hello" | "hi" | "heya" | "hey" => "hello to you too!" | |
;; | _ => "Nice to meet you!" | |
;; }; | |
;; Equivalent in clojure will require the external core.match library | |
;; However, i'm using lumo-cljs, so i'll have to make due with a | |
;; purely clojure equivalent | |
(def message "hi") | |
(defn reply [message] | |
(condp contains? message | |
#{"Reason's pretty cool"} "Yep" | |
#{"good night"} "See ya!" | |
#{"hello" "hi" "heya" "hey"} "hello to you too!" | |
;; else | |
"Nice to meet you!" | |
)) | |
(println "Result:" (reply message)) | |
(println "Result 2:" (reply "good night")) | |
(println "Result 3:" (reply "...")) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "\nExample 3 - Reactive (Rx) programming built in the language)") | |
;;input.onKeyDown | |
;; .where((e) => e.ctrlKey && e.code == 'Enter') | |
;; .forEach((e) => dispatch(addTodoAction(e.target.value))); | |
;; The equivalent to this would be watchers on clojure atoms | |
(def *value (atom 0)) | |
(add-watch *value :value-watcher | |
(fn [key ref old-state new-state] | |
(println "*value changed!") | |
(println "Old Value: " old-state) | |
(println "New Value: " new-state) | |
)) | |
(swap! *value inc) | |
(reset! *value 10) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "\nExample 4 - Implicit Name") | |
;;strings.filter{ it.length == 5 }.map{ it.toUpperCase() } | |
;; This seems like a pointless feature. Anonymous functions with | |
;; method dispatch already resolves this. | |
(require '[clojure.string :refer [upper-case]]) | |
(def strings ["here" "are" "a" "bunch" "of" "interesting" "strings"]) | |
(println "Result:" (->> strings | |
(filter #(= (count %) 5)) | |
(map upper-case))) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "\nExample 5 - Destructuring") | |
;;let someInts = (10, 20); | |
;;let (ten, twenty) = someInts; | |
;;type person = {name: string, age: int}; | |
;;let somePerson = {name: "Guy", age: 30}; | |
;;let {name, age} = somePerson; | |
;; Equivalent is very well supported in clojure. It is probably even | |
;; more powerful than javascripts implementation | |
(let [some-ints [10 20] | |
[ten twenty] some-ints] | |
(println "Result 1:" ten twenty)) | |
(let [some-person {:name "Guy" :age 30} | |
{:keys [name age]} some-person] | |
(println "Result 2:" name age)) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "\nExample 6 - Cascade Operator") | |
;;querySelector('#button') // Get an object. | |
;; ..text = 'Confirm' // Use its members. | |
;; ..classes.add('important') | |
;; ..onClick.listen((e) => dispatch(confirmedAction())); | |
;; When working in clojurescript, you can use the (doto ...) function. | |
(comment | |
(doto iframe | |
(.setAttribute "id" vimeo-iframe-id) | |
(.setAttribute "src" src) | |
(.setAttribute "width" "100%") | |
(.setAttribute "height" "100%") | |
(.setAttribute "frameborder" "0") | |
(.setAttribute "webkiteallowfullscreen" true) | |
(.setAttribute "mozallowfullscreen" true) | |
(.setAttribute "allowfullscreen" true))) | |
(println "Example in comments") | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "\nExample 7 - If expressions") | |
;;val result = if (param == 1) { | |
;; "one" | |
;;} else if (param == 2) { | |
;; "two" | |
;;} else { | |
;; "three" | |
;;} | |
;; Equivalent in clojure is cond expressions | |
(def param 1) | |
(println "Result:" | |
(cond | |
(= param 1) "one" | |
(= param 2) "two" | |
:else "three")) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "\nExample 8 - Try expressions") | |
;;val result = try { | |
;; count() | |
;;} catch (e: ArithmeticException) { | |
;; throw IllegalStateException(e) | |
;;} | |
;; The equivalent in clojure | |
(println "Result:" | |
(try (count 0) | |
(catch js/Object e | |
(println "Throw Exception") | |
"Here, have a value"))) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "\nExample 9 - Automatic currying") | |
;;let add = (x, y) => x + y; /* same as (x) => (y) => x + y; */ | |
;;let five = add(2,3); /* 5 */ | |
;;let alsoFive = add(2)(3); /* 5 */ | |
;;let addFive = add(5); /* y => 5 + y; */ | |
;;let eleven = addFive(6); /* 11 */ | |
;;let twelve = addFive(7); /* 12 */ | |
;; Currying in clojure isn't very well supported, but also not very | |
;; useful. Some related things in clojure are defining n-arity | |
;; functions and transducers. | |
;; Reference: https://clojure.org/reference/transducers | |
;; The closest example to the above: | |
(def add (fn [x y] (+ x y))) | |
(def five (add 2 3)) | |
(def also-five ((partial add 2) 3)) | |
(def add-five (partial add 5)) | |
(def eleven (add-five 6)) | |
(def twelve (add-five 7)) | |
(println "Result:" eleven twelve) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(println "\nExample 10 - Method extensions") | |
;;public extension Int { | |
;; func clamp (_ min: Int, _ max: Int) -> Int { | |
;; return Swift.max(min, Swift.min(max, self)) | |
;; } | |
;; | |
;; func abs () -> Int { | |
;; return Swift.abs(self) | |
;; } | |
;;} | |
;; | |
;;1.clamp(0, 10); // 1 | |
;;20.clamp(0, 10); // 10 | |
;;-3.abs(); // 3 | |
;; Equivalent in clojure is protocols, which is probably even more powerful | |
(defprotocol SomeMaths | |
(clamp [this min max]) | |
(abs [this])) | |
(extend-protocol SomeMaths | |
js/Number | |
(clamp [this min max] | |
(cond | |
(> this max) max | |
(< this min) min | |
:else this)) | |
(abs [this] | |
(if (< this 0) | |
(- this) | |
this)) | |
js/String | |
(clamp [this min max] | |
(let [c (count this)] | |
(cond | |
(> c max) (.substr this 0 max) | |
(< c min) (apply str this (repeat (- max c) ".")) | |
:else this))) | |
(abs [this] (.toUpperCase this))) | |
(println "Result(1):" (clamp 23.5 10 20)) | |
(println "Result(2):" (clamp 15 10 20)) | |
(println "Result(3):" (clamp 5 10 20)) | |
(println "Result(4):" (abs -12.5665)) | |
(println "Result(s1):" (clamp "Hellooooooooooaahhh" 5 10)) | |
(println "Result(s2):" (clamp "Hey" 5 10)) | |
(println "Result(s3):" (clamp "Hello" 5 10)) | |
(println "Result(s4):" (abs "Hello")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment