WonderScript
A simple lisp for web development
Synopsis
user> (+ 3 4)
=> 7
user> (defn square (x) (* x x))
=> #js/function "function(x) { return (x*x); }"
user> (square 5)
=> 25
user> (reduce + (range 10))
=> 45
Language
Special Forms
def
quote
cond
fn*
(a direct mapping of JS function semantics)set*
(a direct mapping of JS assignment semantics)js*
loop
recur
-repeat
?throw
(not implemented)try
,catch
,finally
do
(a block with it's own environment),begin
(an immediately executed block with no environment)rescue
,ensure
,else
new
.
Operators
(treated specially by the compiler)
Consider making is possible to define within WS, they would function as low-level code generating macros that can also (optionally) be used as functions.
mod
1<
,>
,>=
,<=
1not
or
,and
bit-not
,bit-or
,bit-xor
,bit-and
,bit-right-shift
,bit-left-shift
,unsigned-bit-right-shift
identical?
==
(identity, i.e. object equality) 1equiv?
(JS type coercive equality)instance?
1typeof
1+
,-
,*
,/
1array-get
,array-set!
,array-length
slot
,slot-set!
,slot?
,length
Equality
=
value equality==
object identity=~
pattern match overloaded by different classes by implementing match(value)
Types of Types
- Type Aliases
deftype
- Union
(->or Number String)
,(->and Number String)
- Class
defclass
- Multiple inheritance (Look at PicoLisp, Dylan, CLOS), or no inheritance either way encourage composition via protocols, method / function composition.
- Protocol
defprotocol
- Record
defrecord
Meta Object Protocol
- Invokable
- Function
- Method
- Class
- Protocol
- Record
- GenericFunction
- Definition
- Context
- Module
- Object
Functions
(fn (x) (+ 1 x))
, #(+ 1 %)
Generic Functions
Methods
Can be dispatched on any type and arbitrary Generic Functions
Modules
The broadest context for state. With the macro forms defconst
and defvar
module level constants and dynamically
scoped variables can be defined. By convention constants are spelled $contstant
, and variables are spelled
*variable*
. Constants and variable can be accessed globally when scoped with the module name i.e. $Module::constant
or *Module::variable*
. By convention modules names are camel cased. All other definitions with in a module must be
explicitly exported and imported to be used. Keywords that are prefixed with a ::
like ::keyword
are automatically
expanded into :Module::keyword
. Modules can be nested inner modules can be accessed with the same notation as other
definitions i.e. OuterModule::InnerModule
. Definitions specified with def
and relatives, defn
, defmacro
,
defclass
, deftype
, defprotocol
, defrecord
are namespaced by their module and private unless exported.
Definitions can be exported with the module
form, and imports can be specified with the use
form. use
with or
without imports makes the modules and all shared definitions accessible (scoped by the module name).
(module Dragnet
(export View TemplateView PageView Button Link))
(use Web
(import html css js))
Exports and shared symbols can also be specified with meta data on the symbol:
(module Web)
(defn ^:export html
(form) ...)
(module Dragnet)
(defclass View ...)
Definition Meta Data
:private
(only seen in module defaults to true):export
(definition can be exported):macro
(definition is a macro):doc
(doc string of the definition):typedef
(boolean, definition is a type alias):tag
(Symbol type tag of the def):sig
(the type signature of a function)
Data Structures
Protocols
- Null
- Numeric
- Boolean
- Meta
- Reference
- Associative - Associate one value with another, lookup values in constant time
- Indexed < Associative - numerically associative
- Named
- Sequence
- Sequencible
Classes
- Value
- Object : Reference
- Nil < Value : Null
- Unset? < Value : Null (state of an unset key in an Associative data structure)
- Undefined? < Value : Null (state of an undefined symbol)
- NaN < Value : Null (state of an undefined numerical operation)
- True < Value
- False < Value
- Symbol < Value : Named, Meta
- Keyword < Value : Named
- Pair < Value : Sequence
- List < Value : Sequence, Meta
- LazyList < Value : Sequence
- Range < Value : Sequence
- Map < Value : Associative
- Dictionary < Object : Associative
- Set < Value : Associative
- MutableSet < Object : Associative
- String < Value : Indexed
- Buffer < Object : Indexed
- Vector < Value : Indexed
- Array < Object : Indexed
- Function < Value
Protocols
A collection of properties/shapes and doc strings
- Meta
- meta()
- set-meta(key, value)
- get-meta(key)
- Value
- hash-code()
- Named?
- name()
- namespace()
- Collection
- add(col, value)
- Seq < Collection
- first()
- next()
- Sequenceable < Collection
- seq()
- Associative < Sequenceable
- get(key, alt = nil)
- remove(key)
- Indexed < Associative
- at(index)
- ImmutableStack
- pop()
- peek()
- push()
- MutableStack
- pop()
- push()
- Queue
- Matchable =~
- match(pattern)
- Equality =
- equal(other)
- Comparable <=>
- cmp(other)
- js/ArrayLike
- length:number
Core Library
=
(value equality)=~
(matching)fn
(lambda macro with arity checks and arity polymorphism)defn
defmacro
deftype
defclass
defprotocol
defconst
defvar
set!
raise
, (builds on throw, requires a class if no class is provided defaults to RuntimeError)if
,if-not
,when
,unless
+
,-
,*
,/
,mod
<
,>
,>=
,<=
,<=>
inc
,dec
identity
,,constantly
always
comment
even?
,odd?
zero?
,pos?
,neg?
true?
,false?
reduce
,map
,filter
,grep
,mapcat
,concat
,reduce-right
,first
,next
,rest
,second
,cons
,drop
,take
,empty?
each
,tap
,dotimes
,doeach
for
,while
,until
,nth
at
range
partition
pr
p
,pr-str
inspect
,print
str
number?
,string?
,boolean?
,function?
set?
,map?
,iterator?
,get
array-like?
,array?
,->array
,array
,slice
,push!
,pop!
,shift!
,unshift!
object?
,undefined?
,null?
,nil?
memoize
,compose
,apply
set-meta
,meta
,get-meta
,reset-meta
atom
,reset!
,swap!
,deref
,compare-and-swap!
(TODO)freeze!
,frozen?
,clone
,immutable?
,mutable?
deftest
,is
defclass
(defprotocol Invokable
"The interface for all invokable objects"
(invoke (*args)))
(defprotocol Type
(satisfies (object)))
(defclass MethodSig
(has Symbol name)
(has-many Symbol arglist)
(has? String doc))
(defclass Protocol :does Type
(has-many? Protocol ^:key protocols)
(has-many MethodSig ^:key signatures)
(has? String ^:key doc))
(defclass Method :does Invokable
(has Symbol name)
(has? String doc)
(has-many Symbol arglist)
(has-many Form body))
(defclass Property
(has Symbol name)
(has Boolean required :default true))
(defclass Class :does Type
(has? String doc)
(has-many? Protocol protocols)
(has-many Property properties)
(has-many Method methods))
Based on https://opendylan.org/documentation/intro-dylan/objects.html
(defclass Vehicle
(has serial-owner)
(has owner))
(defclass Vehicle
(has Integer serial-number :key :sn)
(has? String owner
:key :owner ;; true would work just as well here
:default "Northern Motors"))
TODO
- Add test suite
- Remove Namespaces (use JS modules instead)
- Add syntax objects & syntax quoting
- Improve error reporting and stack traces
- Add generic functions
- Add protocols (implement protocols for existing objects/methods)
- Implement browser-based IDE
- Add abstractions for browser APIs
- Add a database interface
- Datalog based by default
- Abstract over SQL and KV stores
- DBI-like interface for SQL stores
- Implement ST or CL-like images called a "world" (a reified notion of static and dynamic state)
- Create new compilers based on "world" objects
- Create encoders for world objects so they can be persisted and transmitted
- Update JS
- Make use of
let
andconst
- Make use of
class
- Refactor into modules
- TypeScript
- Make use of
Author
Delon Newman [email protected]