Goals (high-level design criteria)
- Minimize the time it takes for an experienced Jasper programmer to read and grok someone else's Jasper code (note: this combines and replaces the criteria of readability and conciseness; sometimes a program can be too concise in that it takes even the most experienced programmer longer to understand the code than it would have taken if it were written in a more verbose manner; and on the other hand sometimes a program can be written so verbosely that although each line is easy to comprehend, there are so many lines that it takes a long time to read)
- Minimize the time it takes to write correct code
- Maximize code reusability
- Minimize community fragmentation
- Maximize metaprogrammability. It should be easy to make Jasper work like your favorite parts of your favorite language. There should never be a need for boilerplate. Metaprogramming should not just be possible (e.g. by writing a macro that parses code), but rather, constructs that assist in expressing metaprogramming should be provided and the set of such constructs should be itself extensible, similar to the way that library functions add to the set of available functions. However, the goal of Jasper's metaprogrammability should mainly be to enhance readability, not to obscure it.
- Maximize interoperability with other languages. It should be easy to start using Jasper to extend your existing codebase in the JVM, in C, in in Python, in C#, in shell, and someday in PHP, Perl, Ruby, Lua, and Javascript, Scheme, and Haskell. You should be able to call and be called from each of these languages with no unnecessary header files and pass native objects back and forth with no unnecessary explicit casting. Jasper shall be implementable on top of the JVM, C, the CLR, and Lisp.
- On static typing. We consider a type system to have 3 functions: (a) turning the compiler into a theorem prover to partially prove the correctness of the program; (b) ad-hoc polymorphism, which allows one symbol to have multiple meaning which are disambiguated by the type context in which it is used; (c) compile-time optimization, namely, by knowing what type things are the compiler might be able to optimize better sometimes. We will neglect (c) since Jasper is not initially concerned with efficiency, and (b) because it can be achieved just as well with dynamic typing. The downsides of static typing are that it takes the programmer some extra effort to tell the compiler what type everything is (even if there is type inference, because in this case you end up debugging the type inference when you make a mistake), and furthermore it is difficult to make type systems which are both enough to cover most use cases without repeated code, but also concise and easy to use. Therefore, Jasper' approach is to be dynamically typed, but to allow optional type annotations. Compiler modules will be provided with an API to perform type inference on these annotation that other modules can access, and an API to do compile-time type-dependent optimizations.
- Minimize special cases
Guidelines (mid-level design criteria)
- Simple things should be simple, complex things should be possible (Alan Kay)
- Principal of Least Surprise
- Regularity: "All manipulable entities should be first-class objects" (Kernel)
- Metaprogrammability: "Programmer-defined facilities should be a ble to duplicate all the capabilities and properties of built-in facilities" (Kernel)
- Protecting you from yourself: "Dangerous computation behaviors (e.g., hygiene violations), while permitted on general principle, should be difficult to program by accident" (Kernel)
- Local readability: you should usually be able to figure out what a line of code is doing by reading that line, the function mentioned in it, and a few of their dependencies; but without reading the entire module (inspired by http://www.reddit.com/r/programming/comments/evbyu/why_lisp_is_a_big_hack_and_haskell_is_doomed_to/c1b9y1s )
- Minimize core constructs
- Explicit is better than implicit. (from Python) (for readability)
- There should be one-- and preferably only one --obvious way to do it. (from Python) (for readability)
- Batteries included. (from Python). We'll have libraries and a CPAN analog, but we want to do this even more, by using a governance process to promote libraries up a chain of official standardization (apparently Java does this well, todo, look into their process).
- Small number of powerful orthogonal basic operations (if they are powerful, they will be used in preference writing little libraries, and ppl will be able to read each others' code; if they are orthogonal, it will be more likely that there will only be one obvious way to do a given thing, and ppl will be able to read each others' code; this also aids implementation on top of other languages)
- Simple syntax. (makes metaprogramming easier)
- each accessible, named function is another bit of complexity for a reader to decipher. Sometimes it is better to put a lot of functionality into one procedure, rather than to split it into many tiny named helper functions. Except that, if these are private 'inner functions' in a small scope, then the reader can quickly see every place where they could be called from.
- (low) latency over throughput
- concurrency over single-threaded speed
Non-goals
As important as setting goals is setting non-goals. Of course, many of these will be things that would be desirable, but one must make tradeoffs.
- Approchability for novice programmers: Jasper wants to be the best language for the best programmers. We won't leave out a powerful construct because it is hard for beginners to grok. We won't bother to make the syntax look like other languages. We won't keep the standard library small just so that it can be learned quickly.
- Sparseness: unlike Python, we encourage dense (e.g. a lot of stuff happens on each line) code
- Smallness of interpreter/compiler; it's okay to make the compiler do a lot of work (e.g. trampoline for greenthreads, tail call optimization) in order to make the constructs exposed to the programmer powerful and simple.
- As fast as C. We don't have to be as fast as C, or as memory-efficient as C, even in theory assuming a perfect compiler. We do have to do about as good as Java or Go (in theory assuming a perfect compiler); so within an order of magnitude of C.
- However, reading about how the language feature of computed goto is necessary to make it feasible to write a bytecode dispatcher with reasonable performance, this makes me think that perhaps Jasper should be efficient enough to make if feasible to implement interpreters for arbitrary other languages on top of Jasper with only a reasonable slow-down. This may be a niche task, but it seems like an important one to me for the purpose of metaprogrammability.
- safety-critical: again, it should be POSSIBLE to in the future use Jasper for safety-critical work, but for the forseeable future, the implementation will not be solid or stable enough to support that
use-cases
- quick scripting
- an interpreter
- web apps
- numerical data exploration/analysis, interactive or not
- text editing, IDE
- desktop apps
- version control
Strategies (low-level design criteria)
- Optional static typing. The type system is conceptualized as, at compile-time, a theorem prover that helps you partially prove the correctness of a program, and at run-time, a way to efficiently and implicitly generate sanity checks. It also assists in conciseness by allowing the meaning of code to be different depending on the types of the values it is dealing with (a form of context), e.g. polymorphic dispatch based on type.
- small 'core' language which is then extended via metaprogramming in the std lib to std Jasper
- both core Jasper and std Jasper should be concisely implementable in std Jasper
Prioritization
De-prioritized:
- Initial run-time and space efficiency. It's important that programs in the language could, in theory, be compiled to run reasonably quickly with a reasonably small footprint; we might have to rule out language features that prevent this. It's not a priority to actually do this early on. In fact, early on, i won't even worry about whether the language could even in theory be efficient, although if i notice something wrong, or if someone points something out, i'll start thinking about how to change it.
- Backwards compatibility. If the language takes off, maybe this'll change, but until then we don't care one whit about backwards compatibility (however, one test of Jasper's metaprogrammability might be how easy it is to write Jasper programs that transform old-style Jasper into new-style upon each backwards-incompatible update).
- Compile-time efficency: a neat rule someone told me: don't add a compiler optimizatino unless either it reduces the time it takes the compiler to compile itself, or it is for something for a domain that isn't much present in the compiler's own source code (e.g. 3d rendering).