A Proposition for a new Language
- Is a lisp: There’s always something to be said for homoiconicity.
- Is neither a lisp-1 or lisp-2: It’s like a lisp-1 in that there is only 1 namespace, and like a lisp-2 in that there is a seperation between functions and other values. It would have static typing to differentiate functions and other values (but in the lisp tradition, dynamic typing for everything else). The reason for this will become clear shortly.
Lowers the barrier to entry by allowing infix function form: By knowing what is and is not a function at compile time, it is possible to freely put functions anywhere in the s-expression and let the compiler move them to the beginning. Normally an expression like
(1 + 2)would be an error because 1 is not a function, but the programmer knows that+is a function and that that’s what needs to be evaluated. With static function typing, the compiler will rewrite expressions to evaluate the first function in the expression.You can also sharpquote any function that you don’t want to be evaluated, allowing infix functions like compose:
(#'reverse o #'sort). I choose sharpquote because it servers a purpose similar to sharpquoting in common lisp, but a shorter prefix might be preferable.Has literal data structure syntaxes: Clojure has it right with how it treats non-cons-cell data structures, but the syntax interferes with standard lisp conventions, for example:
(cond [(cons? n) (...)] [else (...)])Instead of how clojure uses different baracket types directly, it requires a
#prefix before the brackets:- Vectors use the mathematical angle bracket notation:
#<1 2 3>. - Sets use parenthesis:
#(1 2 3) - Maps use the standard curly brackets:
#{:hello "world" :foo "bar"} - Tables (addressed later on) use square brackets, and start with a list of fields:
#[@field1 @field2 @field3] - Linked lists are just quoted lists:
'(1 2 3)
Of course all of these a just shorthand for calls to
vector,set,map,table, etc.- Vectors use the mathematical angle bracket notation:
Has SQL-like tables: Relational databases provide a very expressive way to query data efficiently, so why not have it native in the language, complete with a literal syntax for tables and field names? Field names are prefixed with an
@character and by convention are only used as field names in literal tables, and in queries.Table literals are represented as a list of field names, followed by the rows of the table. The number of field names at the beginning of the table is used to determine the number of columns in the table. An example table could be something like:
#[@id @u-name @p-word 1 "adrusi" "f6632c24dd46eb3982fa936c9832da8c" 2 "jdoe" "a31405d272b94e5d12e9a52a665d3bfe"]Queries are essentially SQL, but with more lisp-like names and syntax:
(def users #[@id @username 1 adrusi 2 jdoe 3 pancakeman49]) (def follow-relations #[@id @follower-id @followee-id 1 2 1 2 3 1 3 1 2] (defn (followers-by-username username) (select (select (select all-fields from users as followers where (eq? (followers @id) (relevant-relations @follower-id))) from follow-relations as relevant-relations where (eq? (relevant-relations @followee-id) (followee-user @id))) from users as followee-user where (eq? (followee-user @username) username))))The select syntax is just a macro that normally expands to calls to
map,filterand other such functions (which are defined generally and work on tables as a list of maps).Also shown in that example is that table rows are functions of their field names (and vice-versa). Not shown are other structures like joins and unions which also work as expected.
Apart from being an expressive addition to the language, built in SQL also helps libraries remain consistent. There should also be a hook for the same syntax to work for other database systems, ones outside of RAM. These won’t have access to the full language in
whereandonclauses, but otherwise should look the same, which should also help with scalability. When making a quick prototype you use a database in RAM, but then as needed you can migrate to sqlite, and then to postgres, without changing any query code.Algebraic data types. These would be available for use in pattern matching, and could also be optionally used to generate compile time warnings. All standard library functions would be implemented in terms of algebraic data types and type classes unless they don’t fit that model.
- Pattern matching is a must in every new languages, and it works especially well with with algebraic data types which let you define your own patern-matching structures.
- A logic programming library. Prolog works very well for some use-cases, but is just too pure for most. However, logic programming can work very well for certain calculations within a larger system, so embedded prologs are a welcome addition to every language.
Well that’s a wrap, I would strongly appreciate comments on what you like and don’t like.