mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			368 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			368 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ;; # 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))
 |