← Back to team overview

ooc-dev team mailing list archive

Request for comments - Arrays (Literals, Types, C Arrays, Stack allocation, Optimizations), and Constructors

 

Hi everyone,

These last few days, I've been busy fixing bugs to make rock compile itself.
Things have been going pretty smoothly, as it now parses 57 files (see log
http://gist.github.com/316144 ) before choking, out of the 137 that rock+sdk
consist of.

But now I've reached a point where I have to make choices that might differ
from j/ooc's behavior. Here are my ideas/plans, I'd love to hear your
comments on it.

*Arrays
=====
*
*    Literals*

How to determine the type of [1, 2, 3.14] ? Several cases.
If we're in a var-decl, take the 'largest' type, e.g. one in which all
values fit (in this case, Float). In the case of [animal, dog], it would be
'Animal'
In the case [1, 2, dog], an error should be thrown.

If we're not in a var-decl, chances are there are 'outer requirements', e.g.
a function's signature, an outer cast, etc. as in the following cases:

a := [1, 2, 3] as Float[]
a : Float[] = [1, 2, 3]
call: func (f: Float[])
call([1, 2, 3])

In all these cases, the array type can be easily deduced (and verified, and
an error thrown if an incompatible element is in.)

*    Types*

structs/Array should be gone. It provides no advantage over ArrayList
(neither in size nor speed), and we want no redundancy, right?

a := [1, 2, 3]
..should be an ArrayList<Int>

a : Int[3]
should be a shortcut for
a := ArrayList<Int> new(3)

call: func (f: Float[])
..should be an alias for:
call: func (f: List<Float>)

This means we'll move structs/ArrayList and structs/List to lang/, where
it'll get imported implicitly in every source file. This is, imho, a small
cost to pay for classes that are used in 83% of the ooc code out there
anyway. (Yes, that's an HIMYM reference.)

*    C arrays* (aka raw arrays)

You can still use gc_malloc() anywhere, handle them as pointers:

a := gc_malloc(3 * Int size) as Int*
for(i in 0..3) a[i] = i

C arrays will now *always* have to be defined with the 'char**' style, never
'char[][]', cause '[]' will be reserved to ArrayList

    *Stack allocation

*We've been over this numerous times on IRC, and there were heated debates.
Of course, allocating on the stack is faster. And it's also more dangerous
(you have to be careful not to leak references to it to outer functions, and
it's hard to keep an eye on the stack usage, which size may vary from
platform to platform, and in case you allocate too much you have a
difficult-to-debug problem on your hands).

I've always thought that memory allocation is almost always a job best left
to the compiler (hence the choice of a GC). rock will be an amazing
playground to implement stack-allocation, capture-into-the-heap etc.
Application developers should have time to concentrate on cleaner code
instead!
*
   Optimizations

*It is well known that sometimes C is slower than, say, Fortran
because of pointer
aliasing <http://en.wikipedia.org/wiki/Pointer_alias>. Making pure ooc
arrays would allow optimizations in an assembly backend for rock, that the C
backend wouldn't allow (except maybe with the use of the
http://en.wikipedia.org/wiki/Restrict keyword).
That's assuming we're willing to heavily inline functions of ArrayList, of
course. But there are fascinating perspectives for an asm backend that I'm
looking forward to explore.

*Constructors
==========*

In j/ooc, constructors (e.g. the init method) couldn't be overloaded. You
had to use different prefixes in each subclass. This behavior, is not
desirable.

In rock, currently, init methods can be overriden, but this also means you
can do

Animal: class {
  init: func { "New animal created!" println() }
}

Dog: class extends Animal {
  init: func ~withName (=name) { "New dog %s created!" format(name)
println() }
}

d := Dog new("Dogbert") // creates a dog alright
d2 := Dog new() // whoops, d2 is an Animal, not a dog :/

In Java, calling 'Dog new()' would be illegal. Should we forbid it in ooc
too? (Based on the name of the method, even if it's user-defined).
I think it's a wise choice, except it will maybe require more boilerplate
code in some classes.

On the other hand, I'd still like to keep init()/new() as close as possible
to 'normal' methods, the only trickery involved being that defining an
init() instance method generates a corresponding new() static method.
Maybe we should simply forbid to call static functions defined in parent
classes, for *all* static methods? That would solve the problem and be
consistent. Besides, who does that anyway?



That's it for tonight, I guess. In the absence of comments, I'll follow the
plan I have outlined here, but I'd love to hear your thoughts!

Amos Wenger, aka nddrylliog

Follow ups