;; # wisp ; Wisp is homoiconic JS dialect with a clojure syntax, s-expressions and ; macros. Wisp code compiles to a human readable javascript, which is one ; of they key differences from clojurescript. ;; ## wisp data structures ;; 1. nil - is just like js undefined with a differenc that it's ;; not something can be defined. In fact it's just a shortcut for ;; void(0) in JS. nil ;; => void(0) ;; 2. Booleans - Wisp booleans true / false are JS booleans true ;; => true ;; 3. Numbers - Wisp numbers are JS numbers 1 ;; => 1 ;; 4. Strings - Wisp strings are JS Strings "Hello world" ;; Wisp strings can be multiline "Hello, My name is wisp!" ;; 5. Characters - Characters are sugar for JS single char strings \a ;; => "a" ;; 6. Keywords - Keywords are symbolic identifiers that evaluate to ;; themselves. :keyword ;; => "keyword" ;; Since in JS string constats fulfill this purpose of symbolic ;; identifiers, keywords compile to equivalent JS strings. (window.addEventListener :load handler false) ;; Keywords can be invoked as functions, that desugars to plain ;; associated value access in JS (:bar foo) ;; => foo["bar"] ;; 7. Vectors - Wisp vectors are JS arrays. [ 1 2 3 4 ] ;; Note: Commas are white space & can be used if desired [ 1, 2, 3, 4] ;; 8. Maps - Maps are hash maps, plain JS objects. Note that unlike ;; in clojure keys can not be of arbitary types. { "foo" bar :beep-bop "bop" 1 2 } ;; Commas are optional but can come handy for separating key value ;; pairs. { a 1, b 2 } ;; In a future JSONs syntax may be made compatible with map syntax. ;; 9. Lists - You can't have a lisp without lists! Wisp has lists too. ;; Wisp is homoiconic and it's code is made up of lists representing ;; expressions. The first item in the expression is a function, being ;; invoked with rest items as arguments. (foo bar baz) ; => foo(bar, baz); ;; # Conventions ;; Wisp puts a lot of effort in making naming conventions transparent, ;; by encouraning lisp conventions and then translating them to equivalent ;; JS conventions: (dash-delimited) ;; => dashDelimited (predicate?) ;; => isPredicate (**privates**) ;; => __privates__ (list->vector) ;; => listToVector ;; As a side effect some names can be expressed in a few ways, although ;; it's considered to be an advantage. (parse-int x) (parseInt x) (array? x) (isArray x) ;; # Special forms ;; There are some functions in wisp that are special, in a sence that ;; they compile to JS expressions & can not be passed around as regular ;; functions. JS operators are represteted in wisp as special forms ;; Arithmetic forms - Wisp comes with special form for arithmetic ;; operations. (+ a b) ; => a + b (+ a b c) ; => a + b + c (- a b) ; => a - b (* a b c) ; => a * b * c (/ a b) ; => a / b (mod a b) ; => a % 2 ;; Comparison forms - Wisp comes with special forms for comparisons (identical? a b) ;; => a === b (identical? a b c) ;; => a === b && b === c (= a b) ;; => a == b (= a b c) ;; => a == b && b == c (> a b) ;; => a > b (>= a b) ;; => a >= b (< a b c) ;; => a < b && b < c (<= a b c) ;; => a <= b && b <= c ;; Logical forms - Wisp comes with special forms for logical operations (and a b) ;; => a && b (and a b c) ;; => a && b && c (or a b) ;; => a || b (and (or a b) (and c d)) ;; (a || b) && (c && d) ;; Definitions - Variable definitions also happen through special forms. (def a) ; => var a = void(0); (def b 2) ; => var b = 2; ;; Assignments - In wisp new values can be set to a variables via `set!` ;; special form. Note that in functional programing binding changes are ;; a bad practice, avoiding those would make your programs only better! ;; Stil if you need it you have it. (set! a 1) ;; Conditionals - Conditional code branching in wisp is expressed via ;; if special form. First expression following `if` is a condition, ;; if it evaluates to `true` result of the `if` expression is second ;; expression otherwise it's third expression. (if (< number 10) "Digit" "Number") ;; Else expression is optional, if missing and conditional evaluates to ;; `true` result will be `nil`. (if (monday? today) "How was your weekend") ;; Compbining expressions - In wisp is everything is an expression, but ;; sometimes one might want to compbine multiple expressions into one, ;; usually for the purpose of evaluating expressions that have ;; side-effects (do (console.log "Computing sum of a & b") (+ a b)) ;; Also number of expressions is `do` special form 0 to many. If `0` ;; result of evaluation will be nil. (do) ;; Bindings - Let special form evaluates containing expressions in a ;; lexical context of in which simbols in the bindings-forms (first item) ;; are bound to their respective expression results. (let [a 1 b (+ a c)] (+ a b)) ;; Functions - Wisp functions are JS functions (fn [x] (+ x 1)) ;; Wisp functions can be named similar to JS (fn increment [x] (+ x 1)) ;; Wisp functions can also contain documentation and some metadata. ;; Note: Docstring and metadata is not presented in compiled JS yet, ;; but in a future it will compile to comments associated with function. (fn incerement "Returns a number one greater than given." {:added "1.0"} [x] (+ x 1)) ;; Wisp makes capturing of rest arguments a lot easier than JS. argument ;; that follows special `&` simbol will capture all the rest args in array. (fn [x & rest] (rest.reduce (fn [sum x] (+ sum x)) x)) ;; Overloads - In wisp functions can be overloaded depending on number ;; of arguments they take, without introspection of rest arguments. (fn sum "Return the sum of all arguments" {:version "1.0"} ([] 0) ([x] x) ([x y] (+ x y)) ([x & more] (more.reduce (fn [x y] (+ x y)) x))) ;; If function does not has variadic overload and more arguments is ;; passed to it, it throws exception. (fn ([x] x) ([x y] (- x y))) ;; ## Other Special Forms ;; Instantiation - In wisp type instantiation has a consice form, type ;; function just needs to be suffixed with `.` character (Type. options) ;; More verbose but JS like form is also there (new Class options) ;; Method calls - In wisp method calls are no different from function ;; calls, it's just method functions are perfixed with `.` character (.log console "hello wisp") ;; Also more JS like forms are supported too! (window.addEventListener "load" handler false) ;; Attribute access - In wisp attribute access is also just like function ;; call. Attribute name just needs to be prefixed with `.-` (.-location window) ;; Compound properties can be access via `get` special form (get templates (.-id element)) ;; Catching exceptions - In wisp exceptions can be handled via `try` ;; special form. As everything else try form is also expression. It ;; results to nil if no handling takes place. (try (raise exception)) ;; Although catch form can be used to handle exceptions (try (raise exception) (catch error (.log console error))) ;; Also finally clase can be used when necessary (try (raise exception) (catch error (recover error)) (finally (.log console "That was a close one!"))) ;; Throwing exceptions - Throw special form allows throwing exceptions, ;; although doing that is not idiomatic. (fn raise [message] (throw (Error. message))) ;; ## Macros ;; Wisp has a programmatic macro system which allows the compiler to ;; be extended by user code. Many core constructs of Wisp are in fact ;; normal macros. ;; quote ;; Before diving into macros too much, we need to learn about few more ;; things. In lisp any expression can be marked to prevent it from being ;; evaluated. For instance, if you enter the symbol `foo` you will be ;; evaluating the reference to the value of the corresponding variable. foo ;; If you wish to refer to the literal symbol, rather than reference you ;; could use (quote foo) ;; or more usually 'foo ;; Any expression can be quoted, to prevent it's evaluation. Although your ;; resulting programs should not have these forms compiled to JS. 'foo ':bar '(a b) ;; Wisp doesn’t have `unless` special form or a macro, but it's trivial ;; to implement it via macro. Although let's try implemting it as a ;; function to understand a use case for macro! ;; We want to execute body unless condition is `true`. (defn unless-fn [condition body] (if condition nil body)) ;; Although following code will log "should not print" anyway, since ;; function arguments are exectued before function is called. (unless-fn true (console.log "should not print")) ;; Macros solve this problem, because they do not evaluate their arguments ;; immediately. Instead, you get to choose when (and if!) the arguments ;; to a macro are evaluated. Macros take items of the expression as ;; arguments and return new form that is compiled instead. (defmacro unless [condition form] (list 'if condition nil form)) ;; The body of unless macro executes at macro expansion time, producing an ;; if form for compilation. Which later is compiled as usual. This way ;; compiled JS is a conditional instead of function call. (unless true (console.log "should not print")) ;; Simple macros like above could be written via templating, expressed ;; as syntax-quoted forms. ;; `syntax-quote` is almost the same as the plain `quote`, but it allows ;; sub expressions to be unquoted so that form acts a template. Symbols ;; inside form are resolved to help prevent inadvertent symbol capture. ;; Which can be done via `unquote` and `unquote-splicing` forms. (syntax-quote (foo (unquote bar))) (syntax-quote (foo (unquote bar) (unquote-splicing bazs))) ;; Also there is a special syntax sugar for both unquoting operators: ;; Syntax quote: Quote form, but allow internal unquoting so that form ;; acts as template. Symbols inside form are resolved to help prevent ;; inadvertent symbol capture. `(foo bar) ;; Unquote: Use inside a syntax-quote to substitute an unquoted value. `(foo ~bar) ;; Splicing unquote: Use inside a syntax-quote to splice an unquoted ; list into a template. `(foo ~bar ~@bazs) ;; For expmale build-in `defn` macro can be defined expressed with ;; simple template macro. That's more or less how build-in `defn` ;; macro is implemented. (defmacro define-fn [name & body] `(def ~name (fn ~@body))) ;; Now if we use `define-fn` form above defined macro will be expanded ;; and compile time resulting into diff program output. (define-fn print [message] (.log console message)) ;; Not all of the macros can be expressed via templating, but all of the ;; language is available at hand to assemble macro expanded form. ;; For instance let's define macro to ease functional chanining popular ;; in JS but usually expressed via method chaining. For example following ;; API is pioneered by jQuery is very common in JS: ;; ;; open(target, "keypress). ;; filter(isEnterKey). ;; map(getInputText). ;; reduce(render) ;; ;; Unfortunately though it usually requires all the functions need to be ;; methods of dsl object, which is very limited. Making third party ;; functions second class. Via macros we can achieve similar chaining ;; without such tradeoffs. (defmacro -> [& operations] (reduce (fn [form operation] (cons (first operation) (cons form (rest operation)))) (first operations) (rest operations))) (-> (open tagret :keypress) (filter enter-key?) (map get-input-text) (reduce render))