3 ms·
Yes… let's talk macros first. Actually, first first you should look at multi-dimensional analog literals if you haven't seen it, http://weegen.home.xs4all.nl/
by kenferry 15y ago
Yes… let's talk macros first. Actually, first first you should look at multi-dimensional analog literals if you haven't seen it, http://weegen.home.xs4all.nl/eelis/analogliterals.xhtml http://weegen.home.xs4all.nl/eelis/analogliterals.xhtml. Awesome/yikes.
Okay, back. What are the reasons to consider macros? Probably, compile time checking and ability to directly use variables that are in scope (e.g., fooButton for some button).
The goal of compile time checking is really debugging, to make it easy to find and fix mistakes. Now, in a format string, we don't have compile time checking, but we can have really good, descriptive error messages. Our diagnostics were inspired by those in clang, http://blog.llvm.org/2010/04/amazing-feats-of-clang-error-recovery.html http://blog.llvm.org/2010/04/amazing-feats-of-clang-error-re..., right down to the carat pointing at the problem within the code. Maybe we could have done that with macros? I guess I didn't really try that hard, but usually if you mess up a macro, you're going to get something really cryptic from the compiler. Also, it's wayyyy easier for the client to grok and feel comfortable with a format string than with a crazy macro system. You know how we're parsing the string. You could implement it yourself if you had to. It's understandable, and you can see that there won't be side effects. Now consider that multi-dimensional analog literals link above. Does it work? Heck yeah! Do you want black magic like that in your code, with behavior you don't understand (at least without way more investment in understanding than we want to require)? Probably not! Plus, we're likely to have to compromise on the visual design of the language now and/or in the future to contort it into what's technically possible in macros over ObjC. This will also hurt debugging, in that you'll be less likely to quickly understand what your code says. So for debugging, I don't think macros are actually going to be a win, despite the compile time checking. And really, the above concerns are enough to kill macros for me.
Plus, we might be able to recover most of the compile time checking anyway. I'd love to see clang's __attribute__ format extended to do checking of Cocoa visual format strings. http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#index-Wformat-2426 http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#i.... We have a bug tracking this.
Ability to directly use variables is a positive for macros. As is, we have to pass a dictionary on the side that maps identifiers to actual view objects. In python you could pass locals(), but ObjC doesn't maintain a symbol table at runtime. For this we actually do pull a skankier-than-usual macro trick to make things easier.
id views = NSDictionaryOfVariableBindings(fooButton, barButton);
expands to something equivalent to
id views = _NSDictionaryOfVariableBindings(@"fooButton, barButton", fooButton, barButton, nil);
and _NSDictionaryOfVariableBindings parses the comma separated string to make a dictionary mapping @"fooButton" -> fooButton.
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
APPKIT_EXTERN NSDictionary *_NSDictionaryOfVariableBindings(NSString *commaSeparatedKeysString, id firstValue, ...); // not for direct use
Okay, last, why not make this an actual part of Objective-C, like regexes are in some languages or like LINQ is in C#.
I feel like the reasoning here is more wishy-washy, so I don't know, maybe it's wrong and we'll revisit it. Our system has a continuum from leaf code on which nothing else depends, like apps, down through AppKit, Foundation, and Objective-C. The continuum isn't really defined by what framework some code is in, it's defined by how much sits on top of it in the dependency stack. As you go down toward the deeper and more built-upon parts of the system, reliability becomes more and more important, and our ability to cheaply change our minds if an interface isn't working out goes down. The standards for simplicity and wide applicability go up and up. Objective-C's deep. You can literally learn _all_ of Obj-C in a weekend if you know C. We like that. In recent releases we have relayered things like memory management (GC, ARC) and properties from being framework conventions to being formal parts of the language. But that's where the language is right now. There's a pretty clear divide between language and framework, and the language doesn't reach up and violate the layering much. If we're thinking about moving something like these layout format strings into the language, we should also be thinking about printf-style format strings (or other templating), Core Data predicates, regular expressions and lots of other Cocoa-ness - basically obliterating the existence of any kind of layer boundary between ObjC and Cocoa. These would be extremely controversial changes, inside and outside of Apple. That doesn't necessarily make them wrong, but I'm not confident they're right. Anyway, it's something that could be considered in the future.
- Someone 15y agoIn my/the ideal world, clang would have some plugin feature where it knows that it must pass that string to some external DLL for validation. Ideally, that DLL should be able to generate code (just as the C preprocessor), but I fear that would require quite some interface between plug-ins and clang. I think it already would be a big win if that DLL only could use the compiler state to issue error messages. Open problem is how clang would know that a string needs external validation. I think a variant on the @"string" format might work: id myConstraint = @layout"[button]-[textField]"; id myRegex = @regex"^[a-z]+$"; (I say might because the C++ grammar has some corners that may deliver surprises here; with (im)proper function overloading, the compiler might find a way to interpret @layout as taking the address of layout) The link between prefix and DLL should be a command-line argument to clang. Problem, of course, is that you give up compatibility with other compilers. Also, this is more clunky than it needs to be; in a 'proper language' like Lisp the integration would be seamless.
- dirtyaura 15y agoThanks for an awesome answer. I agree that your approach with format strings is right, but it was great to hear reasoning against the other options. I agree that macros are dangerous for an embedded language. In our iOS C++ framework, they have their uses, mainly if we want to reduce boilerplate code, when - bridging ObjC/C++ worlds - creating e.g. record data types with JSON parsing & other common functionality for records - creating handle objects for refcounted objects but for more complex "embedded languages" we have resorted to C++ templates. Properly used, the give us very nice APIs, but build times are quite bad. Sidenote: I'd love to get an email notification (opt-in per my comment) from HN if someone answers/comments on my comment. Your answer went unnoticed by me for 2 weeks.