-
-
Save od0x0/1044107 to your computer and use it in GitHub Desktop.
Features (I don't care about the syntax for this at this point): | |
1. Ignore the underlying ABI that is compiled to | |
Reasoning: Portability. A good example of how to handle things like this is with .NET/C#, where any name can be used as an identifier, but if it has other syntax nastiness around it (like being a keyword or having spaces in between) it can be prefixed with a @ or surrounded with []. Obviously we'd have to go with something different, but the same concept can be applied. This would allow compatibility with other languages that may (will!) have a different set of keywords than the ones we use in ooc | |
Alternative: We could just ignore the idea of a direct name mapping of structures/functions and create a JSON "interface" file, blah, blah, blah or do really nasty name mangling. | |
2. GC built into the language as a language triggered automatic reference counting system | |
Reasoning: GC is nice, but for applications where it's crucial to make sure that no sudden pauses of any duration occur, such as high performance games, we can't afford to have a collector in the BoehmGC fashion. The key would to be have the GC as a language handled feature that operates without a runtime. A hybrid reference counted system that works similarly to the Apple blocks mechanism, where it starts on the stack but can be copied and then refcounted if it gets moved "upwards" can help, and the language implementation can step in to handle the reference counting so no manual calls need to be made. Of course, if we target a system with gc built in, like the JVM or CLR, we don't need to worry about this. | |
3. Classes: bye-bye! Prototypes + Interfaces: Hello! (This is major language change, and I doubt it will get implemented for ooc.) | |
Example: | |
Readable: interface { | |
readByte: Func -> Byte | |
} | |
Writable: interface { | |
writeByte: Func(Byte) | |
} | |
dummyfile := object (ObjectBaseClassBlahBlahBlah){ //In the C under layer, this would look like a closure would, with it being a structure of refcount, context, message handler, and superclass (classes would be generated by the language, not the programmer) | |
someProperty := 0 | |
readByte := func(){0} | |
writeByte := func(byte){} | |
} | |
writeMeALlamaLetter := func(out: Writable){out writeByte('l' as Byte)} | |
writeMeALlamaLetter(dummyfile) //Compile time check, if dummyfile does not implement the methods required of it by Writable, this fails | |
//Interfaces can only contain methods or setters/getters, no direct access to fields allowed | |
Reasoning: This allows closures, generics, interfaces, and classes to be unified. It would also potentially simplify the refcounting gc, by only having one type of natively allocatable structure. | |
4. Anonymous types | |
Before, we had | |
Writable: interface { | |
writeByte: Func(Byte) | |
} | |
writeMeALlamaLetter := func(out: Writable){out writeByte('l' as Byte)} | |
But, what if we only wanted the interface for that particular function and didn't want a whole new identifier used. | |
We could do this: | |
writeMeALlamaLetter := func(out: interface {writeByte: Func(Byte)} ){out writeByte('l' as Byte)} | |
We could then define types similarly as in C with typedefs by using "type" | |
Writable: type interface { | |
writeByte: Func(Byte) | |
} | |
"cover" would be removed, and replaced with "struct" | |
Vec3: type struct { | |
x, y, z: Float | |
} | |
Vec2: type struct { | |
x, y: Float | |
} | |
Reasoning: To go along with the prototype idea, and avoid the use of identifiers for one time cases. | |
Obviously, a lot of these features would create potential for massive coding abuses, but they would be helpful and more consistent than the current magic! |
The point is that other than the code required to raise and lower the reference count, there is no runtime that can possibly be spawned. 0 collectors. Of course there will be overhead (roughly equivalent to manual reference counting), but it is likely that it will be the same or, in the more likely case, better than the cost of having a collector thread. As far as the other things, it's all a matter of implementation and goals from what I've seen, and we should be able to handle those issues as we come across them.
Also, taking this another step further, we could even have cases such as:
doSomething: func {
for(i in 0...100) Vec3 new(i, i, i) toString() println()
}
optimized by having the compiler recognize the code and switch allocation methods to a stack for Vec3, and an optimistically small sized stack for the string which can switch to malloc'd memory if it turns out that toString.returns a string too large.
Basically, it's a refernce counting GC (similar to the one that python uses), but with added smartness that we gain from ooc's static typing. See http://en.m.wikipedia.org/wiki/Reference_counting
So when can we start playing with this? ;)
What are the biggest challenges in your opinion to get a GC-free ooc?
Side question - how doable and sane is it today - with Rock 0.9.2 - to make ooc99 code play nice with other languages, for instance an ooc99 library used in a objC project?
The biggest challenge I see would be to have a very clean compiler design that promotes aggressive optimizations. This would probably be something left to oc rather than rock for that reason. I really have no clue how doable it is today, as even though you can create an sdk that doesn't use a garbage collector (as I have done before), the compiler still calls the gc for things like generics.
I really want to believe there's a sane way to make a clean free()-less language without GC.
But are you sure it wouldn't affect portability? What if 2 different C-from-ooc libraries are loaded in C++ code, would two different counting/dealloc systems be spawned?
And wouldn't there be substantial performance trade-offs? Reference counting means an ooc-made library will spawn a reference counting system that will require extra memory and processing. And apparently the current version of ObjC's ARC, according to Apple (see Objective-C Advancements In-Depth), has added some performance overhead (they're still optimizing it). Not to mention it seems quite complex to implement: ARC introduced lifetime qualifiers for edge cases (also complex to understand and use), and AFAIK Python still relies on GC.