TERMINOLOGY_PROPOSAL.md 9.8 KB

Application.Run Terminology Proposal (Revised)

Executive Summary

This proposal addresses specific terminology issues in the Terminal.Gui Application.Run lifecycle while preserving what works well. Based on maintainer feedback, we keep Begin, End, and RequestStop unchanged, and focus on the real sources of confusion.

What Works Well (Keep Unchanged)

  • Begin and End - Clear, concise lifecycle pairing without being wordy
  • RequestStop - Non-blocking nature is appropriately conveyed by "Request"
  • Distinction between RunLoop and RunIteration - RunLoop starts the driver's mainloop, RunIteration processes one iteration

The Real Problems

Problem 1: RunState Sounds Like State Data

Current:

RunState runState = Application.Begin(toplevel);
Application.RunLoop(runState);
Application.End(runState);

Issue: The name RunState suggests it holds state/data about the run, but it's actually:

  • A token/handle returned by Begin() to pair with End()
  • An execution context for the Toplevel
  • Not primarily about "state" - it's about identity/scoping

Proposed Options:

Option A: RunToken

RunToken token = Application.Begin(toplevel);
Application.RunLoop(token);
Application.End(token);
  • ✅ Clear it's a token, not state data
  • ✅ Concise (not wordy)
  • ✅ Industry standard pattern (CancellationToken, etc.)

Option B: ExecutionContext

ExecutionContext context = Application.Begin(toplevel);
Application.RunLoop(context);
Application.End(context);
  • ✅ Accurately describes bounded execution scope
  • ✅ Familiar from .NET (HttpContext, DbContext)
  • ⚠️ Slightly longer

Option C: Keep RunState but clarify in docs

  • ⚠️ Name remains misleading even with good documentation

Problem 2: EndAfterFirstIteration Confuses "End" with Loop Control

Current:

Application.EndAfterFirstIteration = true;  // Controls RunLoop, not End()
RunState rs = Application.Begin(window);
Application.RunLoop(rs);  // Stops after 1 iteration due to flag
Application.End(rs);       // This is actual "End"

Issue:

  • "End" in the flag name suggests the End() method, but it actually controls RunLoop()
  • The flag stops the loop, not the lifecycle
  • Creates confusion about when End() gets called

Proposed Options:

Option A: StopAfterFirstIteration

Application.StopAfterFirstIteration = true;
  • ✅ "Stop" aligns with RequestStop which also affects the loop
  • ✅ Clearly about loop control, not lifecycle end
  • ✅ Minimal change

Option B: SingleIteration

Application.SingleIteration = true;
  • ✅ Shorter, positive framing
  • ✅ Describes the mode, not the action
  • ⚠️ Less obvious it's about stopping

Option C: RunLoopOnce

Application.RunLoopOnce = true;
  • ✅ Very explicit about what happens
  • ⚠️ Slightly awkward phrasing

Problem 3: "Run" Overload (Lower Priority)

Current:

Application.Run(window);        // Complete lifecycle
Application.RunLoop(state);     // Starts the driver's mainloop  
Application.RunIteration(state); // One iteration

Issue: Three different APIs with "Run" in the name doing different things at different levels.

Note: @tig's feedback indicates the distinction between RunLoop and RunIteration is important and understood. The "Run" prefix may not be a critical issue if the distinction is clear.

Possible future consideration (not recommended now):

  • Document the distinction more clearly
  • Keep names as-is since they work with understanding

Recommended Changes

Minimal Impact Recommendation

Change only what's most confusing:

  1. RunStateRunToken (or ExecutionContext)

    • Clear it's a token/handle
    • Less ambiguous than "state"
    • Concise
  2. EndAfterFirstIterationStopAfterFirstIteration

    • Aligns with RequestStop terminology
    • Clearly about loop control
    • Minimal change
  3. Keep everything else:

    • Begin / End - Perfect as-is
    • RequestStop - Clear non-blocking signal
    • RunLoop / RunIteration - Distinction is valuable
    • Run() - Familiar high-level API

Usage Comparison

Current (Confusing):

// High-level
Application.Run(window);

// Low-level
Application.EndAfterFirstIteration = true;
RunState rs = Application.Begin(window);
Application.RunLoop(rs);
Application.End(rs);

Proposed (Clearer):

// High-level (unchanged)
Application.Run(window);

// Low-level (clearer)
Application.StopAfterFirstIteration = true;
RunToken token = Application.Begin(window);
Application.RunLoop(token);
Application.End(token);

Understanding RunLoop vs RunIteration

It's important to preserve the distinction:

  • RunLoop(token) - Starts the driver's MainLoop and runs until stopped

    • This is a blocking call that manages the loop
    • Calls RunIteration repeatedly
    • Returns when RequestStop() is called or StopAfterFirstIteration is true
  • RunIteration(ref token) - Processes ONE iteration

    • Processes pending driver events
    • Does layout if needed
    • Draws if needed
    • Returns immediately

Visual:

RunLoop(token):
  ┌─────────────────────┐
  │ while (Running)     │
  │   RunIteration()    │ ← One call
  │   RunIteration()    │ ← Another call
  │   RunIteration()    │ ← Another call
  └─────────────────────┘

This distinction is valuable and should be preserved.

Migration Strategy

Phase 1: Add New Names with Obsolete Attributes

// Add new type
public class RunToken { ... }

// Add conversion from old to new
public static implicit operator RunToken(RunState state) => new (state.Toplevel);

// Mark old type obsolete
[Obsolete("Use RunToken instead. RunState will be removed in a future version.")]
public class RunState { ... }

// Add new property
public static bool StopAfterFirstIteration { get; set; }

// Mark old property obsolete
[Obsolete("Use StopAfterFirstIteration instead.")]
public static bool EndAfterFirstIteration 
{ 
    get => StopAfterFirstIteration;
    set => StopAfterFirstIteration = value;
}

Phase 2: Update Documentation

  • Update all docs to use new terminology
  • Add migration guide
  • Explain the distinction between RunLoop and RunIteration

Phase 3: Update Examples

  • Examples use new APIs
  • Keep old examples in "legacy" section temporarily

Phase 4: Future Removal (Multiple Releases Later)

  • After sufficient adoption period, consider removing obsolete APIs
  • Or keep them indefinitely with internal delegation

Alternative Naming Options

For RunState/RunToken

Option Pros Cons Recommendation
RunToken Clear it's a token, concise New terminology ⭐ Best
ExecutionContext Industry standard Slightly longer Good alternative
RunHandle Clear it's a handle "Handle" sounds Win32-ish Acceptable
RunContext Familiar pattern "Context" overloaded in .NET OK
Keep RunState No change needed Remains misleading Not recommended

For EndAfterFirstIteration

Option Pros Cons Recommendation
StopAfterFirstIteration Aligns with RequestStop Slightly longer ⭐ Best
SingleIteration Shorter Less obvious meaning Good alternative
RunLoopOnce Very explicit Awkward phrasing OK
Keep EndAfterFirstIteration No change Continues confusion Not recommended

Comparison with Other Frameworks

Token/Context Pattern:

  • .NET: CancellationToken - token for cancellation scope
  • ASP.NET: HttpContext - context for HTTP request
  • Entity Framework: DbContext - context for database session
  • Terminal.Gui: RunToken (proposed) - token for execution scope

Loop Control Flags:

  • WinForms: Application.Exit() - stops message loop
  • WPF: Dispatcher.InvokeShutdown() - stops dispatcher
  • Terminal.Gui: RequestStop() (keep), StopAfterFirstIteration (proposed)

FAQ

Q: Why not change Begin and End to BeginSession and EndSession?

A: Per maintainer feedback, "Session" makes the names wordy without adding clarity. Begin and End are clear, concise, and work well as a lifecycle pair.

Q: Why keep RunLoop?

A: The distinction between RunLoop (starts the driver's mainloop) and RunIteration (one iteration) is important and well-understood. The "Run" prefix is not the primary source of confusion.

Q: Why change RunState?

A: "State" implies the object holds state/data about the run. In reality, it's a token/handle for the Begin/End pairing. Calling it a "Token" or "Context" is more accurate.

Q: Why change EndAfterFirstIteration?

A: "End" in the flag name creates confusion with the End() method. The flag controls loop behavior, not lifecycle cleanup. "Stop" aligns better with RequestStop which also affects the loop.

Q: Is this bikeshedding?

A: No. These specific names (RunState, EndAfterFirstIteration) cause real confusion. The changes are minimal, focused, and address documented pain points while preserving what works.

Summary

Recommended Changes (Minimal Impact):

  1. RunStateRunToken
  2. EndAfterFirstIterationStopAfterFirstIteration

Keep Unchanged:

  • Begin / End - Clear and concise
  • RequestStop - Appropriately conveys non-blocking
  • RunLoop / RunIteration - Distinction is valuable
  • Run() - Familiar high-level API

Benefits:

  • ✅ Eliminates the two primary sources of confusion
  • ✅ Maintains clarity of successful patterns
  • ✅ Minimal disruption (2 names only)
  • ✅ Complete backward compatibility via obsolete attributes
  • ✅ Respects maintainer feedback

This focused approach addresses real problems without over-engineering the solution.