(defprofile lagénorhynque
:name "Kent OHASHI"
:languages [Clojure Haskell Python Scala
English français Deutsch русский]
:interests [programming language-learning mathematics]
:contributing [github.com/japan-clojurians/clojure-site-ja])ClojureScript (CLJS)
-
compiler for Clojure that targets JavaScript
- cf. Clojure (JVM/Java), ClojureCLR (CLR/C#)
-
simple and powerful functional Lisp
Try CLJS with Lumo
$ npm install -g lumo-cljs
$ lumocljs.user=> (defn hello [& {:keys [to]
#_=> :or {to "world"}}]
#_=> (println (str "Hello, " to "!")))
#'cljs.user/hello
cljs.user=> (hello)
Hello, world!
nil
cljs.user=> (hello :to "ClojureScript")
Hello, ClojureScript!
nil-
simple rule:
(op arg ...)- various kinds of
- functions
- macros
- special forms
- various kinds of
-
structural editing
cljs.user=> '(1 2 3) ; list
(1 2 3)
cljs.user=> [1 2 3] ; vector
[1 2 3]
cljs.user=> #{1 2 3} ; set
#{1 2 3}
cljs.user=> {:a 1 :b 2 :c 3} ; map
{:a 1, :b 2, :c 3}cf. function definition
(defn hello [& {:keys [to]
:or {to "world"}}]
(println (str "Hello, " to "!")))
-
compile-time metaprogramming
-
code as data
sexp -> sexp
- e.g.
formacro (sequence comprehension)
;; ClojureScript
cljs.user=> (for [x (range 10)
#_=> :when (odd? x)]
#_=> (* x x))
(1 9 25 49 81)
cljs.user=> (macroexpand-1
#_=> '(for [x (range 10)
#_=> :when (odd? x)]
#_=> (* x x)))
(cljs.core$macros/let
[iter__9116__auto__
(cljs.core$macros/fn
,,,# Python
>>> [x ** 2 for x in range(10) if x % 2 != 0]
[1, 9, 25, 49, 81]-- Haskell
> [x ^ 2 | x <- [0..9], odd x]
[1,9,25,49,81]Create a ClojureScript project with Leiningen
$ brew install leiningen
$ lein new figwheel cljs-demo # e.g. "figwheel" template
$ cd cljs-demo
$ lein figwheel # or `cider-jack-in-clojurescript` on Emacs- editor-integrated REPL (e.g. Emacs & CIDER)
- browser REPL for front-end development
Changes are automatically loaded in the browser
-
no mutations, no side effects
-
high performance
ljs.user=> (conj [1 2] 3) ; add an element to vector
[1 2 3]
cljs.user=> (conj '(1 2) 3) ; add an element to list
(3 1 2)
cljs.user=> (conj #{1 2} 3) ; add an element to set
#{1 2 3}
cljs.user=> (assoc {:a 1 :b 2} :c 3) ; add an entry to map
{:a 1, :b 2, :c 3}-
maps:
get,assoc -
sequences:
first,rest,cons- lazy sequences
-
rare to define something like classes or algebraic data types
-
few data abstractions and many functions
- e.g. maps for modelling entities
cljs.user=> (ns geometry.sphere)
nil
geometry.sphere=> (defn surface-area [{::keys [radius]}]
#_=> (* 4 Math/PI (Math/pow radius 2)))
#'geometry.sphere/surface-area
geometry.sphere=> (defn volume [{::keys [radius]}]
#_=> (* 4/3 Math/PI (Math/pow radius 3)))
#'geometry.sphere/volume
geometry.sphere=> #::{:radius 2}
#:geometry.sphere{:radius 2}
geometry.sphere=> (surface-area #::{:radius 2})
50.26548245743669
geometry.sphere=> (volume #::{:radius 2})
33.510321638291124- e.g. typical sequence manipulations
cljs.user=> (defn leibniz [n-terms]
#_=> (->> (iterate #(+ % 2) 1)
#_=> (map / (cycle [1 -1]))
#_=> (take n-terms)
#_=> (apply +)
#_=> (* 4.0)))
#'cljs.user/leibniz
cljs.user=> (leibniz 1000)
3.140592653839794
cljs.user=> (leibniz 10000)
3.1414926535900345
cljs.user=> (leibniz 100000)
3.1415826535897198-
data-driven/oriented design
-
examples
- specification system
-
similar to Racket's contract system
-
cf. gradual typing
- e.g. core.typed (Typed Clojure)
-
Common mistakes with maps ...
;; typo in key name
geometry.sphere=> (surface-area #::{:radias 2})
0
;; incorrect value type
geometry.sphere=> (volume #::{:radius "2"})
33.510321638291124Introduce clojure.spec
geometry.sphere=> (require '[cljs.spec.alpha :as s
#_=> :include-macros true])
nil
geometry.sphere=> (s/def ::radius (s/and number? pos?))
:geometry.sphere/radius
geometry.sphere=> (s/def ::sphere (s/keys :req [::radius]))
:geometry.sphere/sphere
geometry.sphere=> (s/fdef surface-area
#_=> :args (s/cat :sphere ::sphere)
#_=> :ret number?)
geometry.sphere/surface-area
geometry.sphere=> (s/fdef volume
#_=> :args (s/cat :sphere ::sphere)
#_=> :ret number?)
geometry.sphere/volumeInstrument specs
geometry.sphere=> (require '[cljs.spec.test.alpha :as stest
#_=> :include-macros true])
nil
geometry.sphere=> (stest/instrument)
[geometry.sphere/surface-area geometry.sphere/volume]
※ add test.check as a dependency (cf. CLJS-1792)
# for example
$ lumo -c src:~/.m2/repository/org/clojure/test.check-0.9.0.jarSpec-instrumented surface-area function
geometry.sphere=> (surface-area #::{:radius 2})
50.26548245743669
geometry.sphere=> (surface-area #::{:radias 2})
Call to #'geometry.sphere/surface-area did not conform to spec:
In: [0] val: #:geometry.sphere{:radias 2} fails
spec: :geometry.sphere/sphere
at: [:args :sphere]
predicate: (contains? % :geometry.sphere/radius)
:cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha
10508]
:cljs.spec.alpha/value (#:geometry.sphere{:radias 2})
:cljs.spec.alpha/args (#:geometry.sphere{:radias 2})
:cljs.spec.alpha/failure :instrument
,,,Spec-instrumented volume function
geometry.sphere=> (volume #::{:radius 2})
33.510321638291124
geometry.sphere=> (volume #::{:radius "2"})
Call to #'geometry.sphere/volume did not conform to spec:
In: [0 :geometry.sphere/radius] val: "2" fails
spec: :geometry.sphere/radius
at: [:args :sphere :geometry.sphere/radius]
predicate: number?
:cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha
10508]
:cljs.spec.alpha/value (#:geometry.sphere{:radius "2"})
:cljs.spec.alpha/args (#:geometry.sphere{:radius "2"})
:cljs.spec.alpha/failure :instrument
,,,











