ooc is a modern, object-oriented, functional-ish, high-level, low-level, sexy programming language. It's translated to pure C with a source-to-source compiler. It strives to be powerful, modular, extensible, portable, yet simple and fast.
It is currently under heavy development - a lot cool stuff is happening . Join us at #ooc-lang, follow us on twitter, hack with us on github!
ooc is distributed under the BSD license.
"Hi, softer world =)" println()
That's right. No need for main(), this code will be put in the load function of the module. And now what's the magic behind that?
ooc is statically typed. Which means a variable can't change type. You can declare the type of a variable like this:
age: Int
You can even declare several variables, if you want. And assign them, of course
x, y, z : Float x = 3.0; y = 1.0; z = 6.0
But the compiler is smarter than that - why specify the type when he can infer it?
age := 36 // this is an Int name := "Norma Jeane" // and this.. a String =)
Yup, you guessed it: the parallel thingies start single line comments.
IMPORTANT NOTE: The := operator is decl-assign, e.g. the type of the declared variable is inferred from the right-hand-side expression. Regular assignment is =, as in C, Java, etc.
Sometimes it's more convenient to regroup variables into a class:
Vector3f: class { x, y, z : Float } vec := Vector3f new() vec x = 3.14 vec y = 6.18 vec z = 42.0 // hey, Java/C/C++/C# guys: look! no dots =) printf("Our vector is (%f, %f, %f)\n", vec x, vec y, vec z)
To access a 'member' of the object (like x, y, or z of a Vector3f), just say the name of the object, and the name of the member.
A class is useful because it can contain functions. Functions are a list of things to do. You can call them with parenthesis. Here's a small function:
sayHello: func { "Hello =)" println() } sayHello() // let's call it
Functions can also return things.
theAnswer: func -> Int { 42 } printf("The answer is %d\n", theAnswer()) // yup, that's the printf from C
If you want to be pedantic, you'd write 'return 42' instead. But in absence of a return in a non-void function, the last value is returned. You should always say what you want to return with that little arrow. That way, the compiler can tell you when you forgot to return something, for example.
Of course, functions can also have arguments.
add: func(a, b : Int) -> Int { a + b } printf("1 + 2 = %d\n", add(1, 2))
So back to our Vector class. We can add a 'length' function which will be very useful.
use math Vector3f: class { x, y, z : Float length: func -> Float { sqrt(x * x + y * y + z * z) } } diagonal := Vector3f new() diagonal x = 1.0 diagonal y = 1.0 diagonal z = 1.0 printf("The length of a cube's long diagonal is: %.2f\n", diagonal length())
You still following? Dots allow you to do several things on an object in a single line, for example:
me := RandomGuy new() me eatBreakfast() .drinkCoffee() .yawn() .goBackToBed()
Some smart people call that "message chaining". It's different from cascade calling, though, because eatBreakfast() and his friends don't return anything. Cascade calls are often mis-used in languages that lack chaining (is there a Java in the room?)
Anyway. Back to our Vector class. Even though my friend Steve will be angry at me - I have to tell you you can add constructors to a class. They look like this:
Vector3f: class { x, y, z : Float init: func(x, y, z : Float) { this x = x // 'this' is called 'self' in some other languages this y = y this z = z } }
Woohoo isn't that lengthy? I'd like to shorten it a little. For example, it's a waste of syntax to say that the arguments x, y, and z are floats. Hey, they are members already. You can shorten this a bit:
Vector3f: class { x, y, z : Float init: func(.x, .y, .z) { this x = x this y = y this z = z } }
That's shorter but I still don't like it. We are doing a really repetitive work. It feels like it should be the machine's work. Well, let's make it work for us:
Vector3f: class { x, y, z : Float init: func(=x, =y, =z) {} }
What just happened? A little bit of syntactic sugar, and our func(tion) is now a one-liner. Neato
We can now create vectors like this:
vec := Vector3f new(6.43, 2.7, 4.3) printf("Got (%f, %f, %f)\n", vec x, vec y, vec z)
(Yes, ooc has consistent constructor semantics, which means that new isn't a keyword, but a static function.)
Hmm now more about classes. Classes can be abstract. That's a complicated word for a simple concept - it means they can't be 'instantiated' (i.e. you can't create objects of that type). What use then? Well you can 'subclass' it, giving you a common interface, and allowing a (poor) form of code re-use.
Animal: abstract class { shout: abstract func } Dog: class extends Animal { shout: func { "Woof, woof!" println() } } Cat: class extends Animal { shout: func { "Meoooww!" println() } }
Now the nice thing is: you can define a function that takes an animal, like that:
poke: func (animal: Animal) { animal shout() }
(Like Io, ooc uses a space instead of a dot to separate an object from its member/method you want to access/call. The above is equivalent to 'animal.shout()' in Java, for example)
And you can pass it all kinds of animal
poke(Dog new()) poke(Cat new())
In ooc, we only have single inheritance, e.g. a class only has one super-class. But what if you want to share several common interfaces? For that, there's a thing precisely called an interface. It's like an abstract class, but it can contain only abstract methods.
Let's try a practical example:
Printable: interface { print: func } Cloneable: interface { clone: func -> This // hehe, smart use of 'This'. Explained later. } Document: class { data: String init: func(=data) } implement Cloneable for Document { clone: func -> This { Document new(data) } } implement Printable for Document { print: func { data println() } } // somewhere else in the code list := ArrayList<Printable> new() list add(Document new("This is a test document")) list add(somethingElse) // foreach: iterate over collection for(printable in list) { printable print() }
Uh oh I'm sorry I got excited while writing code and I walked over a few great features pretty fast. Let's take it one at a time.
This refers to the type you are currently defining. More on that later.
ArrayList<Printable> new() makes use of something very useful called.. generics!
If you come from C++ land (bless your soul), you probably know them as templates. And if you come from Java land, you know the name already - but might be frustrated by their weakness in your language. For example, in ooc you can perfectly do this:
import text/StringBuffer printRealType: func <T> (arg: T) { printf("We've got an arg of type %s and size %d, %d\n", T name, T size, T instanceSize) } number : Short = 42 printRealType(number) printRealType("Hey there") printRealType(StringBuffer new()) // for a class, size != instanceSize, because objects are references in ooc // instanceSize is the amount of memory a variable eats
You can also (but that's a bad habit) compare it with another class. That's right, you can access the class of anything:
print: func <T> (arg: T) { if(T == Int) { printf("%d\n", arg as Int) // 'as' allows casting } else if(T == String) { printf("%s\n", arg as String) } } print(1984) print("George Orwell")
But the most interesting (and maybe most idiomatic) use of generics is in collection classes, such as ArrayList, SparseList, HashMap, etc. In fact, their definition looks something like:
ArrayList: class <T> { data: T* // a pointer to T's }
In a world without generics, you had to do strange things. You could use 'Pointer' to represent pretty much anything. But then you had to cast everything out of get() methods. Now you can do:
ints := SparseList<Int> new() ints add(13) lucky := ints get(0)
.. and the compiler knows for sure that 'lucky' is an Int.
Now onto another fine feature. Foreaches. Foreaches is like.. a regular For that gave up alcohol, decided to shave daily, and bought a new suit. It's prettier, and more effective. Where a For would look like:
for(i = 0 : Int; i < 10; i += 1) { printf("%d\n", i) }
... a Foreach looks like:
for(i in 0..10) { printf("%d\n", i) }
As Mr. Christophe himself would say: "less syntactic noise, baby!"
And of course, you can use it with collections:
import structs/ArrayList list := ArrayList<Int> new() for(i in 0..10) list add(i) // oh yeah no needs for brackets for(i in list) printf("%d\n", list[i]) // operator overloading ftw
Hmm what else is left.. oh yeah
Covers. In ooc, you can "Cover" a primitive type, for example here is such a cover in ooc:
String: cover from Char*
It simply says that whenever I use the type 'String', I really mean 'Char*'. (in C, it really is a typedef). But the interesting thing, is now we can add functions to that cover:
String: cover from Char* { length: func -> Int { strlen(this) } }
And now you can do things like:
n := "Beer" length() printf("Beer is %d times better than water\n", n)
Oh you could also cover Int. Let's have some fun:
Int: cover from int { negate: func -> This { -this } } printf("The opposite of 42 is %d\n", 42 negate())
What was that? 'This' is the type you're currently defining. E.g. in this case it's 'Int'. But it saves repeating the same specific name over and over. And when your girlfriend will ask you to rename 'Int' to 'FluffyNumber' because it's 'cute' (that's what she said), you'll only have one occurence of 'Int' to replace.
And now some more goodies. Can haz operator overloading? yup.
Vector3f: class { x, y, z : Float; init: func(=x, =y, =z) } operator + (u, v: Vector3f) -> Vector3f { Vector3f new(u x + v x, u y + v y, u z + v z) } operator -= (u, v: Vector3f) { u x -= v x u y -= v y u z -= v z } operator as (v: Vector3f) -> String { "(%d, %d, %d)" format(v x, v y, v z) }
And then you can do some nice things like:
Vector3f new(1, 2, 3) as String println() // prints (1, 2, 3) xAxis := Vector3f new(1, 0, 0) yAxis := Vector3f new(0, 1, 0) diagonal := xAxis + yAxis diagonal += Vector3f new(0, 0, 1)
*phew* so many things already. Go grab some coffee, if you think it's long.
Functions in ooc can have the same name, but different arguments. It's called polymorphism. But in that case, you must define suffixes for these functions, so that in the generated C code they don't conflict
add: func ~ints (a, b: Int) -> Int { a + b } add: func ~floats (a, b: Float) -> Float { a + b } // and now we can use: add(3, 5) // will use add~ints add(2.14, 4) // will use add~floats add~ints(3.14, 6.2) // will use add~ints and cast everything to Int
C programmers are probably worried at this point - they haven't seen free() calls yet! Yeah, ooc has a Garbage Collector. By default, objects are allocated with the gc, and some time after they've not been used, they're collected, and freed. That's one big worry off your shoulders - and no, it's not necessarily slower. Go read the internets before you say things benchmarks could make you regret ;) If you want to allocate garbage-collected memory yourself, just use gc_malloc, e.g.:
myRawArray := gc_malloc(Int size * 100) as Int*
There's also gc_realloc and gc_calloc (but no gc_free)
Now about organization. In big programs, you'd probably want to split your program in several modules. These modules can be organized in packages. Packages are just folders. Modules are source files. And you can declare any number of classes, functions, and variables, in a a module.
For example we could do a module called 'secret' in 'my/little/secret.ooc', containing the code:
tellSecret: func { "The cake is a lie" println() }
And we could use it from our main application, e.g. secret-teller, in 'secret-teller.ooc',
import my/little/secret tellSecret()
..and when you launch "ooc secret-teller.ooc" it also compiles my/little/secret.ooc, and produces the executable file "secret-teller". That's right - you don't need header files (.h), you don't need to compile them separately - just import everything you need from your main source file, and everything will be compiled just fine.
This way, you can modularize your app/libraries correctly.
Oh, and for libraries there's another trick. Take for example ooc-gtk.
use gtk import gtk/[Gtk, Window] // acts like: import gtk/Gtk, gtk/Window main: func { w := Window new("Hi, world") w setUSize(800, 600) .connect("destroy", exit) .showAll() Gtk main() }
This simple line 'use gtk' refers to a file 'gtk.use' which should be somewhere in your sourcepath, and which content looks like:
Name: GTK+ 2.0 Description: GNU User interface Toolkit Pkgs: gtk+-2.0 Includes: gtk/gtk.h, gdk/gdk.h
So it acts like you imported "gtk/gtk" and "gdk/gdk" yourself, and compiled with:
$ooc myApp.ooc `pkg-config gtk+-2.0`
Of course that's only grazing the surface of the utility of .use files
There's still a lot of things I didn't cover, like... Static members/functions in classes:
Constants: class { PI := const static 3.14159265 } printf("PI roughly equals %f\n", Constants PI)
Pointer operations (postfix: @ = deref, & = addressOf)
i := 42 // i is an int ptr : Int* ptr = i& // ptr is now a pointer to i ptr@ = 24 // the value of ptr is set to 24 printf("i is now %d\n", i) // hint: i = 24 now. raw := gc_malloc(sizeof(Int) * 10) as Int* raw[3] = 14 // array notation (raw + 3)@ = 14 // pointer notation
References:
addThree(number: Int@) { number += 3 } main: func { i := 42 addThree(i&) // difference from C++. Here everything's explicit printf("Now i = %d\n", i) // hint: i = 45 now. }
Including C headers
include stdio, stdlib // those are included by default, anyway include ./myheader // if myheader.h is in the same directory
Cover-ing a C type/structure so you can access it fields, and externing a C function so you can call it:
include time // equivalent to #include <time.h> in C TimeT: cover from time_t Tm: cover from struct tm { tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday: extern Int } main: func { timestamp := time() // timestamp is now a TimeT t := localtime(timestamp&) // t is now a Tm* printf("It's %dh%d.", t@ tm_hour, t@ tm_min) }
Aliasing a C symbol to another name:
// we have to alias the name because functions can't start with // an uppercase letter gc_malloc: extern(GC_malloc) func (SizeT) -> Pointer
Most of the features above are already implemented, or in the process of being implemented. Check out the code at github Stay tuned on the #ooc-lang channel on Freenode, on the blog, and follow us on twitter!
about
blog
docs
community
downloads
rock has preliminary support for interfaces. mixins and namespaced imports are coming up soon! killer, simple syntax as usual.
for the first time, rock, a 10k SLOC pure ooc codebase, compiles under Win32, and produces executables with gcc. party?
blogpost! gtksourceview highlighting for ooc – gedit & anjuta, rock progress, sudoku solver http://bit.ly/dt0GI1
#ooc_lang for the web! fastcgi bindings by joshthecoder: http://bit.ly/9AcGCa
discount bindings (markdown text to html library) for @ooc_lang by @zenhob http://bit.ly/5dg2K7 coolness!
rock has constructors, half generics, operator overloads on the way. commit frenzy! http://bit.ly/5M8syC
finalizers in j/ooc, meta-classes in rock, yajit, deadlogger, woot, oos, arbitrary precision arithmetic http://bit.ly/5kHIcZ
The ooc blog is online again, thanks @aguspiza for reporting the problem!
@aguspiza Ow. Investigating, thanks for reporting!
yajit works again! check it out: http://github.com/nddrylliog/yajit (special thanks to fredreichbier & showstopper)
rock (ooc compiler in ooc) compiles classes and covers =) don't try it at home yet, though - it's still highly experimental
@alejandrocrosa Hmmm, rtfm? http://docs.ooc-lang.org/ooc-slim/executable-size.html ;)
@arvennard The C++ object model is too complex. And the C syntax has many drawbacks. ooc is, imho, much more readable/light/flexible.
(continued)... that change will allow very nice array and map literals, and other data structs will be usable easily too. Stay tuned.
SubProcess new() now takes an ArrayList<String> instead of String*. (breaking change). ArrayList and HashMap will be BasicTypes soon.
#ooc-lang freeimage bindings: http://bit.ly/2UJ4s7
#ooc_lang libdispatch bindings: http://bit.ly/O2Omd
Fun with version blocks http://paste.pocoo.org/show/151214/ shows the ooc object model flexibility once again!
@minnowcoder Do you have examples of 'more involved pattern matching' ? The match is a regular ooc match comparing classes..
Fun with fake variable-length argument lists: http://paste.pocoo.org/show/151147/ (code in comments = future syntax)