Objective Modula-2 wrote:
As I had previously mentioned, it is my understanding that the aim of this part of the discussion was to explore how an interface for GPC to make use of ObjC libraries might be designed and implemented, what the caveats and challenges are etc.
For such an interface, whatever mapping takes place would have to be undertaken in the direction from Pascal *towards* ObjC and at a lower level from GPC's intermediate language *towards* ObjC.
Now, for the sole purpose of a person unfamiliar with ObjC but familiar with C++ to gain a better understanding of ObjC, I can see some value in articulating how certain ObjC semantics might conceivably be mapped in the opposite direction.
However, in order to design and build a useful and convenient interface for using ObjC libraries from within another language, the designers/implementors should be able to think in the ObjC paradigm space. Ideally they would have implemented something meaningful in ObjC/Cocoa/GNUstep.
Sure, but I'll leave this part to those who might actually implement it.
At this stage of the discussion, I find it increasingly difficult to tell whether the articulation of conceivable mappings of ObjC to C++ is actually meant to be for the sole purpose of gaining a better understanding of ObjC itself.
I'm also trying to find out about the benefits of Objective-C as a language (not only as the de-facto interface of Cocoa and GNUstep).
- Use compiler switches, as GPC already does for the 4 supported
 object models.
I personally prefer the language design philosophy where pragmas do not change the meaning of source code, but yes, the use of pragmas is certainly a workable approach to switching between object models.
We're talking about GPC extensions, and GPC works this way massively already.
- Actually support Objective-C syntax (if it doesn't cause too many
 syntax conflicts), whether or not one is trying to keep compatible  objects.
There is an increasing number of languages that follow this approach. It has its advantages but some people argue that the source code is then no longer portable to compilers that do not support the syntax.
Yet, in order to remain source code compatibility with non-ObjC interfacing dialects of the host language, there must be a complete mapping of existing syntax for the native object model to the ObjC object model. If only one piece of additional syntax is added, compatibility is lost.
As it is often difficult to map existing syntax to all the required semantics of a foreign object model, one might hold the view, if we are not going to be compatible anyway, we might as well support ObjC syntax if it makes the task easier.
Agreed. The main question is if the syntax can easily be mixed in. But Bison will tell us so quickly (which is why, if someone goes this route, I'd suggest to add the grammar first and see if it works without conflicts -- if there are (unsolvable) conflicts, it will be early enough to redesign, and one knows specifically what the problematic areas are).
Objective-C has four visibility modes for instance variables: public, package, protected and private. One of those can be made default, for the other three some kind of qualifier will be needed. Possibly such qualifiers already exist in the native syntax.
Except for package (though in BP, private actually means package (unit) wide visibility, not entirely private).
If you allow switching between object models, then it shouldn't matter that the semantics of "private" in one model do not exactly match the semantics of "private" in another model. Clearly in a multiple-object-models language, the programmer needs to be aware of the specifics of each object model he wishes to use in his code.
If there is no reserved word for "package" in GPC to mark the corresponding visibility mode of the ObjC object model, it could either be added (and only be available in when the ObjC object model is active) or another reserved word might be reused in its place, for example "unit".
This shouldn't be a big problem. Actually, what I meant is that semantically, GPC already has the four visibility modes (though I'm not sure if it implements them currently, but if not, it should). Adding a new (conditional) keyword is also not a big deal.
A very important feature in Objective-C is the ability to add a method to a class outside of the scope of the compilation unit where the class is declared. In Smalltalk and Objective-C this is called a category.
IIUC, this works retroactively, i.e. if a module A calls a method of (sends a message to) an object declared in a module B, and module C extends that object via a category, even if neither A nor B know about C, it affects this call/message, right?
Yes, at runtime all methods are equal, there is no difference between those declared within the scope where the class itself is declared and those declared outside that scope. Categories are *lexical* extensions only. They allow spreading methods *lexically* over multiple files. At runtime, the lexical separation becomes invisible.
OK. I'm mostly getting confused by the word "category" which in this sense seems to have no relation to its common language nor its mathematical meaning.
So IIUC, this eliminates many optimization opportunities, since even if all involved class declarations and implementations are fully known, the compiler can't know if some of them won't be modified later with a category.
Objective-C doesn't have templates nor does it require them because you can choose between static and dynamic typing.
^[citation required]
...
We've had this claim in this thread before. I asked the question, which is still unanswered: How do you implement a generic list type with the following properties:
...
So far this question has been ignored. Until I see a valid response, I will ignore further claims that templates are not needed.
This all comes down to different paradigms.
When different paradigms are discussed, it is easy to mistake statements of the form "A is *different* from B" with "A is better than B" and this often leads to a discussion of different paradigms to slide into an argument about personal preferences. I will refrain from debating preferences.
Suppose I said "When eating soup, the Japanese do not require a spoon because they drink their soup from a bowl". Would anybody pick that apart along the lines of "Unless somebody can tell me how the Japanese eat their soup if they don't have a bowl at hand, then I will simply ignore this statement and conclude that the Japanese do require a spoon to eat their soup just like I do."?
Wrong comparison. If it's really the case that I'm missing something [-> drinking from a bowl], then my question would translate to: "Unless somebody can tell me how the Japanese eat their soup without using spoons, I doubt they can eat soup at all", and you could reply, "They drink it from a bowl", and I'd stand corrected.
I didn't make any arbitrary restrictions [-> no bowl]. I asked about a very concrete use-case (list of any give type), and how to implement it. Of course, any features of Objective-C are permissible.
A more technical analogy would be algebraic versus reverse polish notation in electronic calculators. RPN calculators do not require parentheses because they use postfix operators that operate on a stack of intermediate results.
And it can be shown, in a mathematically rigorous way, that anything that can be expressed in of those notations, can be in the other one as well. That's just what I'm asking here. So far you've just said one wouldn't want to do it. (To apply it to the analogy, with a grain of salt, it's as if I asked how you expressed logarithms in your notation, and you said you don't want to write logarithms because in practice you'd approximate them with the first 3 terms of their Taylor series which requires only elementary arithmetics.)
Objective-C is a late bound language, C++ is early bound. That is a major difference in paradigm and as a result certain problems are solved using entirely different approaches.
Well, are they solved? Again, I restate my question: Suppose you use a list and want to be sure that it only contains objects of a certain type.(*) How do you do it? (This is not a rhetorical question, that's the very question whose answer I'm missing.)
- You can add runtime checks (or the language does them automatically). But then, if you get a wrong type, the program will just raise an error (in whichever form). If you shipped the program, it's too late. So this is an unsatisfying solution.
- You can do extensive testing before shipping. But as we all know, testing can only find bugs and never prove the absence of bugs. So you still have no assurance.
- Formal program verification is hopeless for any somewhat bigger program.
- In contrast: With schema types, you declare your types and the compiler does all the checks before the program is even run.
(*) I'd be surprised if I had to give more concrete examples, as in my own code almost 100% of my lists (maps, trees, ...) are of this kind rather than a "list of anything".
In the end it boils down to compile-time vs. runtime checking. Needless to say, I'm a strong proponent of compile-time checking whenever possible. Since Objective-C is a typed language (at least to some degree -- not all variables are of type "id", but often of specific classes), it's not unreasonable to expect this to extend to containers as well.
Besides, you were missing the point which was this: Because ObjC is late bound, you do not require any mappings for templating syntax because there is no such thing.
As you wrote above, it's also about Objective-C as a target language. In this case, it is relevant how to map such features into it. (As far as I see so far, there is no direct mapping, so it would all have to be expanded in the frontend and mapped in a low-level, C-like way.)
In Objective-C constructors and destructors are just messages, so there is no need for special syntax and translations.
foo := [[FooClass alloc] initWithFoo: 123];
The emphasis was on automatic. So can you, as a class designer:
- Enforce certain actions that are always done on instantiation
 (e.g. ensure that certain object fields are always initialized to  certain values, which don't have to be constant, but might be a  running index or something). I imagine this might be possible by  implementing an instantiation method in the class that does this,  but I'm not sure.
Indeed.
By convention, ObjC classes have a method for allocation (called "alloc") and at least one method for initialisation (called "init"). These methods are by default inherited from the super-class. However, you can of course override them with class specific implementations. Typically, a class specific implementation of init would first invoke the super-class' init method and then carry out any class specific initialisations thereafter.
OK.
In any event, a class is always instantiated by sending and alloc message to it and then sending an init message to the newly allocated object. There is no special syntax.
Thus, when you see a class instantiation such as ...
foo = [FooClass alloc] init];
if you did implement a FooClass specific init method, then the init message will invoke that method
if you did not implement a FooClass specific init method, then the init message will invoke the init method inherited from the FooClass' super-class.
Overriding an inherited method is as simple as declaring and implementing the method just like any other method.
But if you just call [FooClass alloc], then init will not be called? This means for the class designer, if you want to enforce something to happen for all new objects, you put it in alloc, not init, right?
- Enforce certain actions that are always done on deletion of an
 object, whichever way this happens.
Same principle as with initialisation.
Again by convention, ObjC classes have a method for retaining an object (called "retain") and a method for releasing and object (called "release"). In addition there is also a method called "autorelease". Again, these methods are inherited from the super-class.
When you send a retain message to an object, its reference count is incremented, when you send a release message to it, its reference count is decremented. If as a result of your release message the reference count goes to zero, the object is deallocated. Thus, even if you send a release message, should there be still other objects who retained the object, it will not immediately be deallocated but only when the last retaining object sent a release message.
Again, you can override the inherited methods with class specific implementations that perform additional tasks.
OK.
In any event, anything that happens to an object during its lifetime from allocation to deallocation happens to it by sending messages to it which invokes methods implemented in the object's class or any of its super-classes (up to the root class). Any of these methods can be overridden. Any of these messages can be intercepted and redirected. The extend of your control is only limited by your knowledge of the API. There is no hidden magic anywhere that only the compiler has access to. Everything is accessible and controllable from within your own ObjC code.
You may notice that I don't consider compiler magic a bad thing in general, but IMHO it can be useful. (And I don't think I'm alone in this -- Pascal does much more "behind the scenes" than C, and most Pascal programmers seem to like it.)
So if you allocate an object, never "retain" or "release" it, and just let it go out of scope, no method will be called (or message be sent), so the class designer has no way to properly clean up?
For the avoidance of confusion I should perhaps mention that when you see other messages being used to instantiate, for example ...
foo = [FooClass new];
or
foo = [FooClass newWithBar: 123 andBaz: 456];
or
foo = [FooClass newFromArray: bar];
these are messages implemented by methods on top of alloc and init. For example method new would invoke alloc and init internally. In other words it is just a convenience method. It is customary for class implementors to provide several newWith... or newFrom... methods as they see fit.
Sure. (In C++ you do the same by implementing several constructors or allocation functions -- which would be global functions, not class-methods, though.)
[fooException raise];
Does this do what raising exceptions in other languages do, i.e. "jump" to the nearest enclosing appropriate catch-block? Or does it just send the message and continue with the next statement?
Depending on how your exception handling code handles the exception. It may resume, it may re-raise or raise another exception or it may abort. Whatever you see fit.
So for the "normal" case (compared to other EH languages), the EH code would jump to the respective catch-block, but this is implemented in plain code? So I assume there's some sort of "nonlocal goto" primitive that's used to implement it?
Frank