9.4 KiB
Perfect! I've completed all the non-human TODOs. Now let me discuss the human TODOs with detailed suggestions:
Human TODOs - Discussion & Recommendations
1. How to identify an unspecified number of arguments?
The Problem: Some operations need variable numbers of arguments:
depthconsumes the entire stack to count itprintfmight take a format string + arbitrary values
Options:
A) Ellipsis notation: ...
(... -- Size) depth: // Entire stack
(String ... --) printf: // String + any number of args
- Pros: Common in many languages, intuitive, clear intent
- Cons: Ambiguous about types, makes type checking harder
B) Array-based variadic: [T]
([T] -- Size) depth: // Takes array of any type
(String [T] --) printf: // String + array of values
- Pros: Type-safe, forces bundling arguments, easier to implement
- Cons: Requires wrapping args in array syntax, less ergonomic
C) Special stack type: Stack<T>
(Stack<Any> -- Size) depth: // Full stack reference
(String Stack<Any> --) printf: // String + stack reference
- Pros: Explicit that it operates on the stack itself, type-safe
- Cons: Adds conceptual complexity, what does consuming
Stack<T>mean?
D) Constraint-based: T... (typed variadic)
(T... -- Size) depth: // Homogeneous variadics
(String Stringifiable... --) printf: // Constrained variadics
- Pros: Type-safe, clear constraints, flexible
- Cons: More complex type system, harder to implement
E) Just use current system (no variadic syntax)
// For depth: it actually just returns depth, doesn't consume
(-- Size) depth: // Just pushes depth
// For printf: use array
(String [Stringifiable] --) printf:
"x=%d y=%d" [x y] printf
- Pros: Simplest, no language changes needed
- Cons:
depthsemantics unclear, printf less ergonomic
My Recommendation: Option E + D (hybrid)
- For now, don't add variadic syntax - use arrays
- Document that
depthis a special zero-argument operator that just pushes depth - If variadics become essential later, add Option D (typed variadics with
T...)
2. Do all inheritances need to be defined before the trait is?
Options:
A) Yes, inheritance must be before trait
[ ::Addable ::Multiplyable ] ::Number inher
{ } ::Number trait
- Pros: Clear declaration order, simpler parser, inheritance is explicit
- Cons: Separates related declarations, two statements needed
B) No, allow inline inheritance
{ } ::Number<::Addable ::Multiplyable> trait
- Pros: Single statement, more compact
- Cons: Confusing syntax, inheritance looks like generics
C) Inheritance in trait body
{
inherits: Addable Multiplyable
// methods here
} ::Number trait
- Pros: Single statement, clear syntax
- Cons: Breaks postfix philosophy, special syntax inside trait body
My Recommendation: Option A (current design)
- Keeps declarations explicit and ordered
- Maintains postfix consistency
- Parser knows about inheritance before processing trait body
3. Is the inher operator required?
The Question: Can empty inheritance work without inher?
Options:
A) Yes, inher is required (current)
[ ::Addable ::Multiplyable ] ::Number inher
{ } ::Number trait
- Pros: Explicit, clear intent, separates inheritance declaration
- Cons: Extra operator, more verbose
B) No, merge into trait operator
[ ::Addable ::Multiplyable ] { } ::Number trait
- Pros: More concise, fewer operators, still clear
- Cons: Array prefix becomes ambiguous - is it inheritance or something else?
My Recommendation: Option A (keep inher)
- Makes inheritance explicit and unambiguous
- Allows future extensions (diamond inheritance rules, conflict resolution)
- Parser can validate inheritance separately from trait definition
4. Consider removing inher and replacing with impl?
4. Consider removing inher and replacing with impl?
The Idea: Could trait inheritance use the same mechanism as trait implementation?
Current System:
// Inheritance
[ ::Addable ::Multiplyable ] ::Number inher
{ } ::Number trait
// Implementation
::Addable { ... } ::i32 impl
Options:
A) Keep separate inher and impl (current)
- Pros:
- Clear distinction between "is-a" (inheritance) and "implements" relationships
- Inheritance affects trait definition itself, impl affects types
- Easier to reason about: traits inherit, types implement
- Can have different rules and semantics for each
- Cons:
- Two operators with similar-sounding purposes
- More language constructs to learn
B) Use impl for both
// Trait inherits from other traits
::Addable { } ::Number impl
::Multiplyable { } ::Number impl
// Type implements trait
::Addable { ... } ::i32 impl
- Pros:
- Fewer operators
- Unified concept: "X implements Y"
- Simpler mental model
- Cons:
- Ambiguous: does
::Addable { } ::Number implmean Number implements Addable or inherits from it? - Can't declare multiple inheritance in one statement
- Would need different semantics based on whether target is a trait or type
- Loses the clear "combine these traits" semantics
- Ambiguous: does
C) Use impl with array syntax
// Multiple inheritance
[ ::Addable ::Multiplyable ] { } ::Number impl
// Single inheritance
::Drawable { } ::GameObject impl
// Type implementation (unchanged)
::Addable { ... } ::i32 impl
- Pros:
- Unified operator, but array syntax distinguishes inheritance
- Still allows single-statement multiple inheritance
- Semantics clear from syntax
- Cons:
- Array prefix is overloaded (sometimes inheritance, sometimes not)
- Potential ambiguity: is
[::Trait1 ::Trait2]always inheritance?
D) Different syntax entirely
// Inheritance with "extends"
::Number extends [ ::Addable ::Multiplyable ] { } trait
// Keep impl for implementations
::Addable { ... } ::i32 impl
- Pros:
- Very explicit and clear
- Familiar to OOP programmers
- No ambiguity
- Cons:
- Breaks postfix notation (extends comes before trait)
- Adds keyword/operator that's not fully postfix
My Recommendation: Option A (keep separate)
Here's why:
-
Semantic Clarity: Inheritance and implementation are fundamentally different:
- Inheritance:
Numberis composed ofAddable+Multiplyable+ more - Implementation:
i32provides behavior forAddableoperations
- Inheritance:
-
Different Use Cases:
inher: Declares trait relationships at trait definition timeimpl: Provides implementations for types (or default implementations for traits)
-
Implementation Flexibility: Keeping them separate allows:
- Different resolution rules for inherited methods
- Diamond inheritance handling
- Multiple inheritance from many traits in one statement
-
Parser Benefits: The
inheroperator clearly signals "the next trait declaration will inherit from these"
Summary of Recommendations
| TODO | Recommendation | Rationale |
|---|---|---|
| Variadic arguments | Don't add special syntax yet. Use arrays for now, document depth as zero-arg. Consider T... syntax only if truly needed later. |
Keeps language simple, arrays work for most cases, can add later if needed |
| Inheritance before trait? | Yes, keep current requirement. inher must come before trait definition. |
Clear ordering, simpler parser, explicit dependencies |
Is inher required? |
Yes, keep inher operator. It makes inheritance explicit and unambiguous. |
Clear intent, allows future extensions, parser can validate separately |
Replace inher with impl? |
No, keep them separate. They represent different relationships with different semantics. | Semantic clarity, different use cases, better tooling support |
Additional Considerations
For Variadic Arguments
If you later decide you do need variadic syntax, I'd suggest this progression:
Phase 1 (Current): No variadic syntax
(-- Size) depth: // Just returns depth
(String [Stringifiable] --) printf: // Use arrays
Phase 2 (If needed): Add typed variadics
(String Stringifiable... --) printf: // Zero or more Stringifiable values
This gives you a simple starting point with a clear upgrade path.
For Inheritance Syntax
The current design is actually quite elegant:
[ ::Trait1 ::Trait2 ::Trait3 ] ::NewTrait inher
{
// Additional methods
} ::NewTrait trait
This clearly shows:
- What we're inheriting from (the array)
- That we're declaring inheritance (the
inheroperator) - The trait being defined (the
traitoperator)
It's verbose but crystal clear, which is good for a systems language.
Would you like me to add any of these design discussions to the specification document itself, or would you like to explore any of these options in more detail?
My choices for 0.6.1:
Following your suggestions:
- Variadic arguments - Don't add special syntax yet
- Inheritance before trait? - Yes, keep current requirement
- Replace
inherwithimpl? - No, keep them separate
My own choice: - Is
inherrequired? - No, merge into trait operator
And for Variadic arguments do add the array of stuff that will be the current system