Skip to content

Instantly share code, notes, and snippets.

@creationix
Last active December 10, 2015 20:08
Show Gist options
  • Save creationix/4486272 to your computer and use it in GitHub Desktop.
Save creationix/4486272 to your computer and use it in GitHub Desktop.
Ideas for a new programming language
// New ideas for a better syntax based on conversation with Zef
print! "Hello World"
// Comments will actually be lua style
// but for now I'll use JS to keep the highlighting working.
// Implementation of if using assert
if := {cond fn|
assert cond! // Abort returning nil if cond! returns a falsy value
fn! // tail call fn
}
// Implement if with else clause
ifElse := {cond yes no|
(cond! ? yes : no)!
}
// function with two block arguments, the ! means to execute
b := false
if! {a < 5} { b = true }
// Though this particular case would be better written as
b := a < 5
// Implementation of while
while := {cond fn| // Takes two parameters, the conditon block and the iteration block
{
assert cond!
fn!
loop
}!
}
// Count from 1 to 100 using while
i := 1 // decare a new local variable i and set to 1
while! { i <= 100 } {
print! i
i = i + 1
}
// Implementation of times
// loops i from 1 to n calling (fn i) for each
times := {n fn|
i := 1
{
assert i <= n
fn! i
i = i + 1
loop
}!
}
times! 10 print // Print from 1 to 10
// lists are simply comma seperated values between slashes
list := /1, 2, 3, 4/
// lists act like maps in syntax
// There is a "length" method that is the number of items in the list
list.length! // -> 4
// Items can be retrieved using integer indexes
list[0] // -> 1
// Negative indexes count from end (index + length)
list[-1] // -> 4
// invalid indexes return nil
list[4] // -> nil
// Assignment replaces values
list[3] = "Frog" // -> "Frog"
// list is /1, 2, 3, "Frog"/
// Removes an item from the list
list.remove! 2 // -> 3
// list is /1, 2, "Frog"/
// If the argument is nil, the last item is removed
list.remove! // -> "Frog"
// list is /1, 2/
// Insert works the other way with the same meaning of the index
// here nil translates to length (after the last item) and 0 is before the first item
list.insert! "Monkey" nil // -> "Monkey"
// list is /1, 2, "Monkey"/
list.insert! 0 0 // -> 0
// list is /0, 1, 2, "Monkey"/
// create an iterator from a list
iterateList := {list|
i := 0
l := list.length!
{
assert i < l
list[i]
loop
}
}
// iterate over the list using this new iterator
next := iterateList! list
{
item := next! // Get the next item in the iterator
assert item ~= nil // Abort if it's nil
print! item // Print it
loop // repeat
}!
// Implement forEach helper to loop over lists
forEach := {list fn|
i := 0
l := list.length!
{
assert i < l // Make sure we're still in the list
fn! list[i] i // call the callback with item and index
loop // do it again
}!
}
// and now printing each item in the list is much simpler
forEach! list print
// Implement map using forEach
map := {list fn|
new := //
forEach list {item i|
new.insert! (fn! item i) i
}
return new
}
// Map over a list
names := map! list {item i|
item.name
}
// Create an empty map
map := <>
// set key-value pairs using [] syntax
map["name"] = "Tim Caswell"
// string keys that are also valid identifiers can use the shorthand
map.title = "Creator"
// Maps have zero default methods, they are dumb data buckets
// But they can hold any key type
map[false] = "Why not?"
map[map] = map // recursion anyone?
// asking for keys that don't exist return nil
map[<>] // -> nil
// setting a value to nil deletes the key
map[false] = nil
// Multiline map literal syntax
map = <
name = "Tim"
age = 30
>
// Singleline map literal (remember semicolons represent newlines)
<name="Compressed";big=false>
// non-ident keys in literals
crazy := <
[map] = true
[<>] = false
>

Goals

  • Minimal, no IO, no module system, tiny stdlib
  • Easy to implement
  • Easy to learn
  • Easy to embed
  • tiny self-hosted compile to C implementation
  • self-hosted compile to JS implementation

Values

  • Simple immutable primitives

  • integers for counting, loops, indexes, offsets, lengths.

  • floats for when you really need them

  • Booleans (true, false)

  • nil (essentially means undefined)

  • utf8 encoded strings

  • mutable data structures

  • dense ordered lists, no holes allowed

  • unordered maps where any value type can be key or value

  • bytearrays for dealing with binary data possible ctype interface using struct definitions

Expressions

  • explicit order of operations using parens
  • Number/Integer -> Number/Integer math (+, -, *, /, %)
  • Bitwise Integer -> Integer math (<<, >>, |, &, ^)
  • Integer -> Boolean math (<. <=, ==, >, >=, !=)
  • Boolean -> Boolean math (and, or, xor, not) (This implementes logical shortcuts and may not eval all arguments)
  • {expr* } block, evaluates to the last expression
  • {param1 param2| expr*} blocks can take arguments
  • closures have a proto reference to their function prototype
  • definition ident := expr, define ident as a local variable and set a value
  • assignment ident = expr return the expression value
  • Call block using expr(arg1, arg2) where args are also expressions

Loops

There are a few keywords that do control flow in blocks and turn them into loops.

Tail recursion is your friend, embrace it!

  • return val? return early from a block with optional return value
  • assert expr val? Return early from a block if the expression is falsy
  • If we ever want an inverse to assert, some ideas are "refute", "abort", "deny".
  • loop args... Make a tail call to self with optional arguments. This is also an early return in order to ensure tail calls.
// Create a map representing a person
tim = {
name: "Tim",
age: 30
}
// Implement a set using the map as the key in this map
team = {
[tim]: true
}
// Or written in long-form
tim = {}
tim["name"] = "Tim"
tim["age"] = 30
team = {}
team[tim] = true
// Or in one expression without temporary variables
{[{name: "Tim", age: 30}]: true}
// Count down from 100 to 1
{|i|
print(i)
i > 0 and proto(i - 1)
}(100)
// Abstracted as a countDown function
countDown = { |i, fn|
fn(i)
i > 0 and proto(i - 1)
}
countDown(100, print)
// Rectangle Factory
Rect = {|w, h|
{
area: { w * h }
}
}
r = Rect(3, 4)
print(r.area())
// Square Factory
Square = {|s|
{
area: { s * s }
}
}
s = Square(5)
print(s.area())
// Shared Implementation of area (code sharing without inheritance)
area = {|w,h| w * h}
Rect = {|w,h|
{
area: {area(w, h)}
}
}
Square = {|s|
{
area: {area(s, s)}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment