- No compulsory type declarations, with type inference for early error detection.
- Everything passed by reference. A type can specify that object identity doesn't have to be preserved, in which case pass-by-value optimization can be done. But that's just an optimization; the conceptual model is that everything is passed by reference.
- Closures (functions that can access the enclosing lexical environment)
- Anonymous functions (functions that don't have a name)
- If objects are supported, they should be just be syntactic sugar for closures.
- Varargs and apply
- Pattern-matching for function calls and variable assignment. This proves incredibly convenient in Haskell.
- Everything is an expression.
- Optional dynamic scoping to set global-like variables for a call (and subcalls).
- Finding functions in a correct module based on the arguments a la OO.
- Modules as objects that can be passed around.
- Definitions (of functions, variables, etc) are executed. A file consists of statements of code, not definitions. Some of these statements when executed may end up defining functions in the current module.
- No braces; use indentation to control nesting.
- Tailcall optimization.
- Threading: In addition to kernel threads (to exploit multiple cores), the language should support co-operative threads hosted within a single kernel thread, because sometimes you want multiple flows of execution without the non-determinism of arbitrary preemption. These are preempted only when they make a blocking call, like I/O or sleep() or an external call (to C or the hosting language) or by an explicit yield(). Since these don't run in parallel and are not preempted, they should be implemented purely in userspace with very fast switches. They can be used as a substitute for generators. The language also has a inter-thread messaging-passing system, like Erlang.
- Modules (or packages or namespaces): what would a module's interface consist of? A bunch of exported functions, since a function is the only true abstraction we have. (Everything else is on top of functions.)
- Lightweight build system -- no need to "compile" code before running it. Compilation really is optimization (in addition to linting), and so it should not be forced.
- Optimization philosophy: The language should aim to maximize programmer productivity. If you have a performance problem, you should be able to use sacrifice productivity for performance, as an explicit decision and incrementally. The core language should not be designed for performance. Some optimization techniques might be type declarations, non-garbage-collected allocation pools, pointers within an unsafe block (as in C#), etc. Similarly, while the usual list should support resizing, insertions and deletion, like Python's list, there should be a fixed-size typed array that you can use when performance matters. The language should also have a Strongtalk-like VM that gives maximum performance with minimum effort, for dynamic code. But it should also compile to machine code if desired.
- No silent arithmetic overflows. Integers are either infinite-size, like in Python, or you get an OverflowException.
24 Jun 2008
The Ideal Language
This is the language I would design today. I haven't used Lisp, Joy, Forth or APL, so I may miss something there. But here's an ideal language that's reasonably close to the mainstream: