Skip to content

Instantly share code, notes, and snippets.

@Morgul
Last active August 29, 2015 14:15
Show Gist options
  • Save Morgul/28ee0b0925c63c28c9c5 to your computer and use it in GitHub Desktop.
Save Morgul/28ee0b0925c63c28c9c5 to your computer and use it in GitHub Desktop.
My Perfect Programming Language
//----------------------------------------------------------------------------------------------------------------------
// General
//----------------------------------------------------------------------------------------------------------------------
// For the most part, it will look like javascript
var foo = "bar";
var bar = "foo";
if(foo == "baz")
{
something.doStuff(foo);
}
// Anonymous functions even work
function(thing1, thing2)
{
return thing1.doStuff(thing2);
}
// Scoping works exactly like javascript
function(thing1)
{
var foo = "3";
// This always refers to the current execution context; meaning the function object in this case
this.bar = "3";
function inner(thing2)
{
foo = 4;
print("scope:", thing1, thing2, foo); // foo is equal to 4
print(this.bar) // 'undefined', because now the execution context is the inner function
}
print("foo:", foo) // foo is equal to "3";
return inner(thing1 + 2);
print("foo:", foo) // foo is equal to 4;
}
//----------------------------------------------------------------------------------------------------------------------
// Atoms
//----------------------------------------------------------------------------------------------------------------------
var foo = `bar`;
var baz = `bar`;
// An atom always equals itself, but only itself
foo == baz; // true
foo === baz; // true
foo == `bar`; // true
foo === `bar`; // true
foo == 'bar'; // false
foo == atom('bar'); // true
//----------------------------------------------------------------------------------------------------------------------
// Literals
//----------------------------------------------------------------------------------------------------------------------
// Supports several literal types
var someString = "This is a string";
var someInt = 3;
var someFloat = 3.4;
var someList = ['This is also a string, in a list.'];
var someDict = { foo: 'This is in a dictionary' };
var someTuple = { "Don't confuse a tuple", "for a dict", 4 };
//----------------------------------------------------------------------------------------------------------------------
// Types
//----------------------------------------------------------------------------------------------------------------------
// Supports (optional) types, as it's strongly typed; but uses duck typing like python
int foo = 3;
string bar = 4; // throws a TypeError
// Functions can be typed, too
void function doStuff(int numTimes, string msg)
{
while(numTimes)
{
print(msg);
numTimes--;
}
}
//----------------------------------------------------------------------------------------------------------------------
// Pattern Matching
//----------------------------------------------------------------------------------------------------------------------
// Pattern matching happens when two function with the same name have different signatures. It is like an extension to
// more traditional function overloading. Some of the things it can do that function overloading can't is supporting
// literals, or pulling out parts of a structure. (Modeled off Erlang)
// If stuff is a string, this function is called
function doStuff(string stuff)
{
doStuff(int(stuff));
}
// If stuff is an int, this is called
function doStuff(int stuff)
{
print('moar stuff!', stuff + 1);
}
// if stuff is anything else, this is called
function doStuff(stuff)
{
print('wrong stuff!');
}
// Match a 2 length tuple, and pull the components out into vars
function doMoreStuff({ thing1, thing2 })
{
print("thing1:", thing1);
print("thing2:", thing2);
}
// Same as above, but with type checking
function doMoreStuff({ int thing1, list thing2 })
{
print("thing1:", thing1);
print("thing2:", thing2);
}
// This matches an empty tuple
function doMoreStuff({})
{
print("nothing to do");
}
//----------------------------------------------------------------------------------------------------------------------
// Classes
//----------------------------------------------------------------------------------------------------------------------
// Classes are first class citizens, however, they should be considered optional. As much as I love object oriented
// programming, there are problems that can be more easily solved by a functional approach. This language should
// support both.
// classes work mostly like es6 classes, combined with c# and python
class SomeClass extends BaseClass
{
// Fields are supported
foo = "bar";
// Private fields are a thing
private _bar;
// As are properties
bar {
get()
{
return this._bar;
}
set(val)
{
this._bar = val;
}
}
// This is the constructor function
constructor(args)
{
this.args = args;
// The `base` variable is available in any class member function; it referes to the base class.
// In this example, we're calling our base class's constructor
base.constructor(args);
}
// Member function
doStuff(int numTimes)
{
while(numTimes)
{
// We can use 'this' to refer to the class instance.
print("Doing stuff:", this.args);
numTimes--;
}
}
// Static functions do _not_ have access to the class instance; and
// there is only one copy of them in memory, ever. They can be called
// directly on the class, ex: SomeClass.doStaticStuff();
static doStaticStuff()
{
print("Static stuff!");
}
// We support python style decorators
@myDecorator('with args!', 3, this.bar)
@simpleDecorator
final()
{
print("This is the end!");
}
}
//----------------------------------------------------------------------------------------------------------------------
// Actors
//----------------------------------------------------------------------------------------------------------------------
// An actor is a light-weight process that executes concurrently to other processes. Actors receive messages sent to them,
// and do work based on those messages. You create one by extending the Actor class, and overriding it's receive function.
// For thread-safety reasons, an actor only has access to itself, or anything sent via a message. This is one example where
// scope tricks will not work. (ex: you can't declare a variable outside an actor, and have it accesible inside the actor.)
// Need to look into zero-copy message passing; if possible we should use that, otherwise, deal with copies.
// Actors take the place of event emitters in Node.js; since they support message passing, _and_ can be scheduled on
// different cores, they are the building blocks of loosely coupled concurrency.
class MyProcess extends Actor
{
// You can pattern match the receive function obviously
receive({ sender, message })
{
// The send function is how you send messages to actors
send(sender, "I got your message!");
}
receive(`stop`)
{
// The exit function stops the process, and return with the specified reason.
exit(`normal`);
}
// Since actors are class instances, they can have other member functions, too.
doStuff(thingToDo)
{
print("Doing stuff:", thingsToDo);
}
}
// You can create a new instance of an actor in two ways. First, manually:
var pid = new MyProcess();
pid.start();
// Or, you can use the spawn function
var pid = spawn(MyProcess, args);
// You can also stop a pid manally (with a reason)
pid.stop(`normal`);
// Or, you can use the kill function
kill(pid, `normal`);
// Also, it would be easy to build something like Erlang's gen_server:
class GenServer extends Actor
{
receive({`call`, from, request})
{
this.handle_call(from, request);
}
receive({`cast`, request})
{
this.handle_cast(request);
}
receive(else)
{
this.handle_info(else);
}
handle_call(from, request)
{
throw new Error("Unhandled call.", from, request);
}
handle_cast(request)
{
throw new Error("Unhandled cast.", request);
}
handle_info(info)
{
throw new Error("Unhandled info.", info);
}
static call(Actor pid, request)
{
send(pid, {`cast`, pid, request});
}
static cast(Actor pid, request)
{
send(pid, { `cast`, request });
}
static info(Actor pid, request)
{
send(pid, request);
}
}
// While that's a simple example, it should illustrate how such a thing could be done.
//----------------------------------------------------------------------------------------------------------------------
// Modules
//----------------------------------------------------------------------------------------------------------------------
// A module is an importable chunk of code, which can expose function, classes, or objects. The modules are es6 moudules.
import "lodash";
import { GenServer, GenFSM } from "servers";
import * as foo from "foo"
import "/foo/bar/baz";
export function doStuff(){ print("stuff!"); }
// See here for more: http://www.2ality.com/2014/09/es6-modules-final.html
//----------------------------------------------------------------------------------------------------------------------
// Other things I'd like to see
//----------------------------------------------------------------------------------------------------------------------
// Some of the other things I'd like to see would be:
// * List comprehensions (modeled off Python)
// * String manipulations (modeled off Python)
// * Additional types: sets, deques, orderedDicts, arrays (aka typed lists)
// * First-class Promise support (maybe make Promise an actor?)
// * Node-like threaded event loop; maybe use libuv?
// * Erlang-like lightweight process scheduling
// * Erlang/node-like REPL console, with attach/detatch support
// * Erlang-like supervisor/monitor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment