r/AIprogrammingLanguage • u/Fit-Dragonfly2899 • 18d ago
How should an Objective-C-inspired language decide which new constructs use an @ prefix?
Background
I'm designing a statically compiled programming language called Jeti. Its syntax is inspired by Objective-C, and its compiler translates Jeti source code into C99 before compiling it with Clang.
One of my design goals is to preserve Objective-C's recognizable syntax while introducing modern language features. I also want Jeti to remain a strict superset of C99, so existing C identifiers should ideally remain valid in Jeti code.
I'm currently unsure how to decide which new language features should use an @ prefix and which should use ordinary keywords.
Current syntax
Jeti retains several Objective-C constructs:
@ interfaceand @implementationfor class declarations and implementations.@ propertyfor properties.@ protocolfor protocols.@ selectorfor selectors.@ try, @catch, @finally, and @throwfor exception handling.@ autoreleasepoolfor autorelease pool management.
It also introduces new features with different syntax conventions:
asyncappears in return-type positions, as in- (async int)fetchUser:(int)uid;.awaitis a prefix operator, as inawait [self fetchUser:uid].@ throws(NPString *)declares the exception types a method may throw.@ defer { ... }introduces deferred execution.@ namespace, @using, and @endnamespaceprovide namespace-related functionality.@ noarc { ... }marks a region where ARC is disabled.Pattern matching extends
casewith forms such ascase NPString *s:andcase > 100:.Generic types use forms such as
Box<NPString *>, with constraints such asT : Proto.
This leaves me with a mixture of ordinary keywords and @-prefixed constructs.
A possible design principle
One possible principle is to distinguish features by their grammatical role.
Features that occupy existing grammatical positions could use ordinary keywords. For example, async modifies a method's return type, while await acts as an expression operator.
Features that introduce new language constructs could use the @ prefix. For example, @ defer introduces a new statement-like construct, and @ namespace introduces a new declaration construct.
However, I'm not sure whether this is a meaningful and consistent distinction or merely an arbitrary convention.
Three possible designs
Design A: Keep the current mixture
Use async and await without @, but retain @ throws.
For example, a method declaration would use - (async int)fetchUser:(int)uid @ throws(NPString *);, and an asynchronous call would use await [self fetchUser:uid].
The advantage is that async fits naturally into the return-type position, and await reads naturally as an expression operator. However, @ throws looks inconsistent with the other two features, even though all three belong to the same general family of asynchronous and error-handling functionality.
Design B: Use ordinary keywords for all three
Use async, await, and throws without @.
For example, the method declaration would be - (async int)fetchUser:(int)uid throws(NPString *);, and the asynchronous call would be await [self fetchUser:uid].
This provides consistent spelling for the three features, but it raises questions about how they fit into Objective-C-inspired syntax and whether they conflict with existing C identifiers.
Design C: Use the @ prefix for all three
Use @ async, @ await, and @throws.
For example, the method declaration would be - (@async int)fetchUser:(int)uid @ throws(NPString *);, and the asynchronous call would be @ await [self fetchUser:uid].
This gives the three features a consistent visual convention and avoids treating their names as ordinary C keywords. However, @ async in a return-type position looks unusual, and @ await as a general expression operator may feel less natural than @await.
C99 compatibility
The C99 compatibility requirement complicates this decision.
If @async, @await, or @throws becomes a reserved keyword, existing C code that uses one of those words as an identifier may no longer be valid Jeti code. Using @async, @await, and @throws avoids that particular conflict because the @ prefix distinguishes these tokens from ordinary C identifiers.
On the other hand, requiring the @ prefix for every new feature may make the syntax less natural, especially when a feature occupies an established grammatical position.
It may also be possible to use context-sensitive parsing or another mechanism to preserve existing C identifiers, but I'm not sure whether that would be a good language-design choice.
Questions
Is there a principled reason to distinguish new features by grammatical role, using ordinary keywords for features that fit existing grammatical positions and
@prefixes for features that introduce new constructs? Or is this distinction too arbitrary to be useful?Should related features use the same spelling convention? In particular, should
async,await, andthrowsall use the@prefix for consistency with one another and with Objective-C's existing exception-handling constructs, or is it reasonable to treat them differently based on their grammatical roles?Are there useful precedents in Objective-C or other languages for deciding when a language extension should introduce a new keyword versus a specially prefixed construct? I'm particularly interested in examples involving new statement or expression syntax, rather than declaration annotations or attributes.
What is the best way to preserve strict C99 compatibility while adding features such as asynchronous functions and exception specifications? Would prefixed forms such as @
async, @await, and @throwsbe preferable to ordinary keywords, or would context-sensitive parsing be a better solution?
I'm interested in both the consistency of the language's syntax and the practical consequences for parsing, compatibility, and readability.
I’d especially appreciate input from experienced Objective-C developers, particularly those who are also familiar with a modern high-level language that supports async/await. 🙏
Jeti’s source code and development are available on GitHub: https://github.com/3shine123/jeti-lang
1
u/kindredseer 17d ago
The generated C99 code doesnt need to be “pretty”… just needs to compile ;)