Cedar--
Cedar-- is an exercise in making a usable language that has the worst syntax imaginable. It was designed in December of 2018, and was implemented in the two weeks following finals week.
Features
Cedar-- is an object-oriented programming language that also supports a procedural paradigm. As such, Cedar-- supports classes (called structs), but it also supports first-class functions and statements.
Cedar-- uses dynamic and duck typing, and has automatic garbage collection, as it is interpreted through the JVM.
Major features that Cedar-- implements are:
- Functions
- Classes (structs)
- Namespaces (collections of functions, structs, and statements to be executed that are contained within a single, named, scope block)
- Scope blocks
- Pattern matching
Syntax and Example Code
The syntax of Cedar-- is god-awful.
The first example of Cedar-- is the cliched "Hello World".
include IO. IO::puts << "Hello World".
First of all, you can tell that the "puts" function is contained within the "IO" namespace. Statements end with a dot ("."), members of structs and namespaces are accessed with the scoping operator ("::"), and arguments are passed to a function using the argument operator ("<<"). Now we'll move into defining functions.
fun fib << n ( if n < 2 ( n >> ret. ) else ( (fib << n-1) + (fib << n-2) >> ret. ) )
This code snippet shows a great deal of new syntax for Cedar--. Functions are defined using the "fun" keyword, followed by the name of the function, then the argument operator ("<<"), then a comma-separated series of arguments. The function body is enclosed by a pair of parentheses. If-else blocks are denoted by the keywords "if" and "else" (note that else-if functionality is provided by using an "elif" block). Variable assignment works from left to right, that is the result of the expression on the left (the value) is assigned to the result of the expression on the right (the target). To return a value from a function, that value is assigned to the reserved "ret" variable. Note that there is one exception to the function definition syntax described above: functions with no arguments. These functions are called "free" functions, and must use the "free" keyword in their definition:
fun free says_hi ( include IO. IO::puts << "Hi". exclude IO. ) free says_hi.
The "free" keyword must also be included in the function call. The reasoning behind this is that, since functions are a first-class variable, there is no discernible difference between a function call with no arguments and simply a function as a variable. The "free" keyword distinguishes the two. Now let's move on to structs.
struct Person ( init << name, age ( name >> this::name. age >> this::age. ) free print_name ( include IO. IO::puts << this::name. exclude IO. ) free print_age ( include IO. IO::puts << this::age. exclude IO. ) )