HOME - RSS

### O ###

21 May 2019 - 9 minute read
I'm going through a bit of a "new blog" rush, so expect quite a few posts now,
but then I imagine they'll tail off over time to a more reasonable trickle.

One of my projects that I've been particularly enjoying is O. For those
unaware, O is a high-level Object-Oriented general purpose programming language.
It takes inspiration from C++, D, C#, and a little from sh/Bash and Ruby. If
you've seen the O-lang category page, then you'll see a little hello world
program there to give just a little taste of the syntax, but there's a lot more
to it than that.

On O's Gitlab page, under docs, there's a directory named "Design Articles" in
which I've been making little articles describing the design and development
process as it happens. Now I have this site, these articles will be appearing
here. I probably won't move all the existing ones over to here because most of
them are moot now anyway and I can do plenty of nice explaining with pretty
examples here. In this post I want to introduce O for anyone that doesn't
already know about it and also explain what the end goal is for O as I haven't
really talked about that at all yet.

### QUICK FEATURE DEMO ###

O will have - among a lot of other things - a pipeline, built in systems for
dependency injection and plugin frameworks, user-definable statements,
functions/methods as first-class citizens, a broad standard library, compile
options to a bytecode or binary, and no null by default. Here's one code sample
that I think fairly well captures the sort of thing I'm going for with O:

import std.io ;

class coolstuff ;

restricted string name ;

int action( int x ) ;

/// Constructor takes the name {this.name} and the action {this.action.body}
↳for the object to do.
this( piped this.name , body this.action.body ) {
	[
		term.writeln( "Tada!" ) ,
		{ name == "jeff" ?? term.writeln( "Hi Jeff" ) ##
		term.writeln( "Hey you're not Jeff!" ) ; }
	][ action( 12 ) ]() ;
}
entrypoint myprogram ;

/// Constructor of the entrypoint is the entrypoint of the program.
this() {
	var object = "jeff" | coolstuff {
		x == 12 ?? return 1 ## return 0 ;
	}
}

This is a rather arbitrary and extreme example, but it covers plenty of little
clever things, so let's start at the top and work our way down:

In the first block we import the std.io namespace which contains the static
term class that provides terminal-related methods and attributes. Next, we
declare our coolstuff class. Class definitions are just the one line and don't
have braces because there is only one in a file so we save ourselves a whole
indent level. In this class, we have an attribute 'name' that's marked as
restricted, which means other classes can read it like a public member, but
only the class itself may write to it. Next, we declare a method called 'action'
that takes an integer called 'x'. Next, we have the constructor for our class.
Note the arguments it takes. Rather than take a new local variable as a
parameter, it's just taking an existing member of the class. In O, this is
totally do-able, and passing in the value will assign it to that member. The
first parameter is also marked as piped, which means it may be passed in
through the pipeline as we see later on. It also assigns to the 'body' of
'action', that being a block that stores the definition of the method. This is
marked as a body, which means the parameter is provided as a statement body in
braces rather than in the section in parentheses.

In the constructor definition, we make an array of blocks with 2 items. We then
index into that array with the return of 'action' when called with its parameter
'x' being 12. Lastly, the parentheses on the end mean that we're invoking the
block we've just selected. What this means is that we're essentially indexing
into an array of locally defined methods and executing one of them, which would
require a switch statement in other languages, which would be less efficient
than just indexing into an array.

Now, our second section is a new file where we define our entrypoint. An
entrypoint is just a class like any other, except it's this class that is where
the program starts. The O compiler will check for a set of method signatures for
constructors and mark the first it finds as the "main" method of the program.
What this theoretically allows is instantiating the entrypoint elsewhere in the
program and using it in other ways. In our constructor, we create a new variable
(using type-inference) named 'object' and set it equal to the result of a
pipeline. We pipe the string "jeff" into a call to coolstuff, which is the
constructor for that class. Note that you don't need to use 'new'. If you use
'new' then memory is allocated immediately for it, but if you don't, then memory
is only allocated when the object is written to, and reads from that class are
replaced with reads of the default values for that class. Both options are
available as sometimes having memory allocated at an obvious time is helpful,
but also, only allocating it when needed can help to optimise memory usage, as
there are some objects that are created that may never be written to, and so
memory is never allocated as it's never needed. Our constructor call doesn't
have parentheses, this is because the only parameters being passed are via the
pipeline and the body. The string is being piped in for our object's name, and
the body of the 'action' is being defined in the body in the method call. Notice
how the body has access to 'x' as it was declared in the declaration of
'action', and how it has to return an int as that's what the definition of
'action' requires.

So, now say we compile and execute our program - what happens? We start at the
entrypoint's constructor, where we declare a new variable 'object' and get ready
to assign it a value. We take the string "jeff" and pass it into 'coolstuff'
along with our block. The string is assigned to object's name, and the block is
assigned to the body of 'action'. As we're calling the constructor, the code
within that is called, which creates the array of 2 blocks, then evaluates
'action(12)', which returns 1, so we invoke item 1 in the array, which checks
the name attribute and calls 'term.writeln( "Hi Jeff" )', printing "Hi Jeff" to
the screen. That constructor then finishes and our new object is assigned to
'object', then we reach the end of the entrypoint method, marking the end of our
program and we exit.

### PLANS FOR THE FUTURE ###

Ha... "quick"... anyway...
This is just a little bit of what I'm hoping to have this language be able to
do. I've finished the lexer for the bootstrap compiler and I've started working
on the parser too. Progress is definitely being made, but it'll be a while
before I'm compiling anything. That said, I have been learning a tremendous
amount working on this and I'm hoping to learn even more as I work more on this.
My end goal for O is to build up a user base and start making real programs with
it. It would be incredible to be able to build a company out of it so I can be
working on this project for a living. That's all a long way away though.

### CATEGORIES ###

O-lang - Programming
HOME - RSS
Copyright Oliver Ayre 2019. Site licensed under the GNU Affero General Public
Licence version 3 (AGPLv3).