Hacker News new | past | comments | ask | show | jobs | submit login
Proposal for a new language that compiles to Javascript
19 points by EGreg on Dec 28, 2010 | hide | past | favorite | 6 comments
Javascript is poised to be the most ubiquitous programming language in the world. It's in web browsers, and now it's making inroads on the server side. The latest engines are able to optimize it to machine code, and its single-threaded operation with asynchronous callbacks allows efficient, multitasking code in a simple paradigm.

But Javascript is a complex language that's not easy to learn, and even harder to master. Also, some things (such as waiting for several objects to be filled asynchronously, and then using them as soon as they are all available) do not have native support in Javascript, requiring tricky libraries for control flow.

Thus I feel there is a need for a simpler language that compiles into Javascript. That would give us the best of all worlds -- a language you can teach to new developers, which encourages good programming practices, and everything written in this language would be easy to understand by others. I'd call the language Q, and here are some properties it would have:

0. You should be able to nest block comments

We are starting small. This is a minor gripe. Basically I should be able to comment out chunks of code which already contain commented-out code, in a non-smart, without causing the compiler to barf.

1. All keywords would be one letter

Benefits:

People know there are no more than 26 keywords, and make sure to learn them all. Learning the vocabulary is literally as easy as learning (a subset of) the alphabet.

You never have to wonder if you are using a reserved word

Terse and efficient to type code, more compact for reading purposes

Encourages variables to have meaningful names. Perhaps i,j,k would not be keywords, but f would be, for example, the keyword "function" in javascript.

Implications: Built-in types would be capital letters. For example:

  var1: S.n // a new string
  var2: N.n // new number
2. Like Python, indentation would begin a new code block. Line breaks would end a statement, unless the parentheses are still open.

Benefits:

No more arguments about brace style, easier-to-read code

No more forgetting to match braces, or even worse "})" vs "}" -- admit it, you had lots of mistakes like that

No more forgetting semicolons, They can still be used if you wanted to place more than one statement on a line. But that would be rare.

More friendly to IDEs. Right now an IDE has a terrible time trying to guess which brace is the ending brace, or whether your statement is really going to the next line or not. When the language is easy for the IDE to understand, it becomes your friend and helps you catch all of this as soon as you hit return.

Implications: Parameter lists containing functions defined inline would continue by means of comma-first style, which although it takes some getting used to, helps you catch many more mistakes.

Example:

  obj.message(param1, f (var1, var2)
   alert(var1)
   alert(var2)
, f (something) // another function r 2 // returns 2 )

3. Use : for assignment, = for comparison, == for javascript's ===

Benefits: (once you get used to typing in the language)

Never make the mistake of making an assignment when you meant to compare, by forgetting to type an extra =. This error is hard to spot.

Assignments are easier to identify. This language also re-uses : for making sure something is of a certain class (thus, a: I instead of "int a" in C.)

Strict-equality is slightly easier to type.

4. Use : and messages to do most of the work.

Benefits:

Function calls, the keyword "new", and even exceptions are just messages. Kind of like in Ruby. Except here, messages trigger events and you can add hooks before and after an event on any object.

  var1: I // declare var1 and assign it to be an integer
var2: I.n(32) : 5 // new 32-bit integer equal to 5 var3: MyClass.n(var1, var2); var4: MyContainerClass.n(MyClass, 2, 3); // some more cool stuff f something(cool:I, fool:MyClass) // do something in the function r 2 // return 2

in this last example, the function was expecting an integer and a MyClass instance, respectively

5. Loops are discouraged, instead we will have the .each(function(i, obj) { }); construct, except with the keyword x

Benefits:

Loops can suck -- there are problems with closures that bite javascript developers in the butt, there are concerns about how many times the loop's condition actually runs, etc.

The container classes should encapsulate their traversal logic, the calling code should be able to "not know" what the logic is, and just use a standard mechanism. So we can still have while() and until() constructs, but we also achieve the .each by sending a message to a container instance, as follows.

Example:

In the following example, we are looping over an array of even numbers 2 through 198 inclusive:

  ([1..100]*2).x (index)
    //do something with this index
    //in this code block

  someCollection.x (obj, key)
    // etc.
6. Exceptions are just messages to an object which may be forked from an earlier exception handling object.

Benefits:

Javascript is bad with catching exceptions when they are thrown a callback passed to an asynchronous function call. Your call stack has long since gone (since your call was asynchronous) and no one is around to catch your exceptions.

There are fewer concepts to worry about. Exceptions are just messages now. The "catch" and "finally" clauses are just methods of the exception handler object.

This would also lead to a nice paradigm for error handling, which is flexible enough to handle error callbacks AND catching exceptions.

Example:

  // here we call some function, passing a callback and catching exceptions
foo.bar(f () // this is some callback defined inline , callback2, // this is some callback stored as a variable ): { 'Some exception type 1': f (param1, param2) // something going on , 'Some exception type 2': errorHandler , 'Some exception type 3': errorHandler , '': f (param1, param2) // this one catches all other exceptions }, f() // this is the finally clause // or rather, the finally handler

We throw exceptions manually with the following

  // throw an exception
  e[exceptionMessage] (params)
  e['Some exception type 2'] (params)
  e.myException(params)
Notice what happens here. This syntax is consistent with just sending messages. You send messages with obj.message for messages whose names you know, or obj[message] for messages whose names are variables.

When exceptions are thrown (whether due to a division by zero, or manually with e.msg()), we go up the function stack until we find an object capable of handling this message.

That function call syntax,

  foo.bar() [: someObject [ , someFunction ]]
is what makes this possible. It takes the "invisible" object that is listening for various messages that are thrown, and extends a copy of it with someObject, before calling the function. Then, the "t" keyword represents this object, except that calling a method in it actually unwinds the stack first.

7. It would also not have case statements and enums. These can all be done using messages. Just like loops, case statements and enums encourage breaking encapsulation... cases and enums should be done with messages, and the calling code shouldn't be responsible for having to know what the messages are, especially if it's just a container "passing along the message".

8. Finally, it would be nice to build in support for some "re-entrant functions", where you could call this function repeatedly while it's going off to do what you wanted, and it puts the calls in a queue, and then calls all the callbacks once the asynchronous processing has been completed. It would also support caching (memoizing) the result of the function call and throttling the use of resources on some semaphore.

I'm not sure how to implement this one, but it would be nice to have as a language construct.

-------------

Wow, this post is long, but I had some of these ideas around for a long time and I have no time to implement something like this. I thought that at least I can propose this to the hackers on here in the hopes that some of this might find its way into an actual implementation someday :)




It doesn't adhere to all of your suggestions, but CoffeeScript looks like a step in the right direction insofar as exposing only the "good parts" of the language while adding on a little syntactic icing.


I think CoffeeScript provides a good DSL over JavaScript as it is. I found the pythonlike syntax pleasant while also easy for a JavaScript veteran to pick up and run with.

Your idea seems like a mix between Java Smalltalk and ruby to me. I personally don't find the mix appealing


Just do it.

I did the same thing for Kira (a PL I wrote to target PHP) and I added closures, static types, namespaces, and a distributed queue. It worked well, but I warn you that the task to maintain, train people, support, and ship a PL to production environments is not easy. I ultimately just switched languages and servers (which I could do because I wrote a PL and just changed how I targeted the code... I did lose the comments thou).

For 8) look at how C# does coroutines. Basically, you have to build a state machine.


Come on - if you don't have time for your idea, then who does? Spend some nights and weekends putting together a basic version and see if peoples get excited about it.


I would rather go in the direction of extending JavaScript in the direction of ActionScript 3. I love Action Script and I feel that JavaScript is a bit hacky for RIA (not clean OOP,refactoring is hard) What do you think?


I agree, but I think you feel that way because AS3 really implements the ECMA standard, with classes and all.

What about it do you like besides having classes?




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: