Open-source licenses for programming languages

Posted: December 6th, 2009 | Author: Mars | Filed under: Reference | 5 Comments »

Python: custom, BSD-like, plus a clause requiring documentation of changes
Ruby: GPL plus some alternate, less restrictive terms
Erlang: MPL plus modifications; I’m not familiar enough with the original to spot the changes
Perl: Artistic license
Go: BSD
Haskell (GHC): BSD
Clojure: EPL – eclipse license – very wordy, but doesn’t seem to require much beyond BSD
Scala: BSD

I’ve always been a fan of the GPL, but I can’t find any compiler codebase that uses it, save GCC. Perhaps this is because compilers are easier to build than language ecosystems.


Sample: object

Posted: December 5th, 2009 | Author: Mars | Filed under: Uncategorized | 2 Comments »

Here’s the Radian version of a demo snippet I saw on the front page of the Ruby site:

import string from radian
# The Greeter class
object Greeter(name):
  var title = string.capitalize(name)
  function salute:
    result = "Hello " & title & "!"
  end salute
end Greeter
 
# Create a new object
g = Greeter("world")
 
# Output "Hello World!"
io->print(g.salute)

The object statement begins a constructor function. This is a lot like a normal function, but it has no result variable: instead, its result is an object, whose members are the items you declare inside the constructor.

There is no ‘new’ keyword; objects are immutable values, like numbers or booleans, and the lifetime of the memory used to store a value is an implementation detail. Instead, you call a constructor when you want an object value, and then do whatever you want with it; allocation of storage is an implementation detail.

Note the last line: it uses an arrow to call the print method, but a period to call the salute function. What’s going on here?

I’d like to step back and reconsider what exactly it is we’re calling an “object”. Conceptually, an object is a package of data which represents some piece of the program’s knowledge about the job it is doing. The program does its work by changing the state of its objects.

In familiar imperative languages like Java, C++, or Python, the conceptual object is associated with a piece of memory; variables are merely references to this bit of storage. In Radian, it’s the other way around; the object is associated with the variable, and the precise bit of storage the data happens to live in is incidental.

In order to alter an object, then, you alter a variable; you do this by putting a new value into it. One common way to get a new object value is to call a method belonging to the object, which returns a modified version of itself. The arrow operator is a piece of syntactic sugar for this very common operation. These two lines are equivalent:

foo = foo.bar
foo->bar

Another way to think about this would be the ‘const’ modifier in C++. If you’re calling a const function, use the period; if you’re calling a non-const, mutating method, use the arrow.


Samples: Hello World and Fibonacci

Posted: December 5th, 2009 | Author: Mars | Filed under: Design | 2 Comments »

Alright then, what does this language actually look like? Let’s dig into some examples. First, of course, the classic Hello World:

io->print("Hello, world!")

The io object is an implicit program argument which lets you talk to the outside world. The print method sends a string to the standard output.

Here’s the Fibonacci function:

function fibonacci(limit):
  var a = 0
  var b = 1
  for i in range(0, limit):
    result = a
    a = b
    b = result + b
  end i
end fibonacci

It’s a keyword-oriented, block-structured language with significant newlines. I considered Python-style indents, but stuck with explicit ends because I like the way they re-synchronize expectations.

Symbols must be declared, and declarations cannot be forward-referenced. Inner blocks can use symbols defined in outer blocks; but code inside a function can only read and not write to a variable defined outside the function.

I’ll probably build a return statement, but for now every function comes with an implicit result variable; the function returns whatever its final value is. Same with multiple assignment – plan to do it, but not a top priority.


Why imperative style?

Posted: December 4th, 2009 | Author: Mars | Filed under: Design | 2 Comments »

I raised two questions in my last post, and I’d like to expand on the second: what is it about the imperative style that makes its marriage with functional semantics worth the effort?

The short answer is that it’s familiar. Whatever the merits of functional style, most people seem to believe that languages which use it look and feel unapproachably weird. Lisp is a sea of parentheses; Haskell is some kind of mutant algebra from Alpha Centauri. And what are all these weird terms – monads, comprehensions, “higher-order” functions, closures, pattern-matching, combinators, recursion? If you’re not already interested in language theory, these concepts pile up into an impenetrable wall.

I want someone who can get work done in Python, Perl, Ruby, PHP, or Javascript to see Radian as a familiar, unthreatening evolutionary increment. They should feel confident that they can step into the new environment, make a few simple adjustments to the knowledge they already have, and successfully take advantage of parallel computing hardware.

I’m not actually trying to build a functional language at all: I’m trying to build a better tool for parallel computing. If there were some way to build this system into an existing language, that would be a far better solution! This is, unfortunately, impractical; with unrestricted access to mutable shared state, an automatic tool simply can’t determine whether two operations can be safely executed in parallel.

My theoretical foundation is Appel’s principle that SSA is functional programming. Instead of starting with an imperative language and converting it into SSA form so I can perform dataflow-based optimizations, I’m starting with a pure functional dataflow semantics, then working backwards to build up a familiar-looking syntax which can control it – while guaranteeing that I never accidentally introduce any feature which could break the dataflow analyzer’s ability to find parallelism.