-
-
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! |
Compiler-assisted reference counting would allow ooc code to be written in the clean free()-less fashion that it is (python and ObjC's ARC use this system), while C++ style new/delete would kill that cleanliness. Code outside of ooc code would not need to have any knowledge of how the ooc objects were allocated. Most libraries (particularly C and C++) follow a convention of I-allocate-I-delete and you-allocate-you-delete, neither of which need any knowledge of ooc's garbage collection or how to manage memory. People who use libraries created by ooc's conversion to C would be unable to tell that the library they are working with has automatic memory management underneath, so portability would be unaffected.
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.
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.
+1 on anything that makes the code more portable. ooc could be an amazing language to create C libraries that can plug-in to any language (c, c++, objc, ruby, python, etc...). I think the fact that it's a hard thing to do with ooc currently is the one reason why this amazing language is still obscure and unknown. ooc's compiled-to-C99 nature could allow developers to progressively adopt the language into their codebases, instead of forcing them to take the jump to adopt a language-bound technology stack for a project, like many other languages require. Portability is the beauty of C. Combine that with the elegance, ease of use and readability of ooc, and developers will be all over this language.
+1 on not depending so hard on garbage-collection, for performance and for the same reason as above. And I enjoy the counting+autoreleasepool approach of Apple, but how portable would that be, when you'd like to write a language agnostic library? Why not take the C++ approach with new and delete?