T4.5: Decision and Control Structures

Knowledge Review - InterSystems ObjectScript Specialist

1. Post-conditionals

Key Points

  • Syntax: COMMAND:condition arguments -- colon between command and condition
  • Compact code: Execute a single command conditionally without IF/ELSE blocks
  • SET:condition: SET:condition var = value -- only sets if condition is true
  • QUIT:condition: Exit a loop or method conditionally
  • DO:condition: Call a method conditionally
  • WRITE:condition: Output conditionally
  • Any command: Most ObjectScript commands support post-conditionals
  • No ELSE: Post-conditionals have no ELSE counterpart -- use IF/ELSE for that

Detailed Notes

Overview

Post-conditionals are a distinctive ObjectScript feature that allows any command to be executed conditionally by appending a colon and a boolean expression directly after the command keyword. This produces concise, readable code for simple conditional operations without the overhead of full IF/ELSE blocks.

Basic Post-Conditional Syntax

 SET x = 10

 // SET with post-conditional
 SET:x>5 message = "x is large"
 WRITE message, !                  // x is large

 // Equivalent to:
 IF x > 5 SET message = "x is large"

 // WRITE with post-conditional
 WRITE:x>5 "Greater than 5", !

 // Multiple post-conditionals (each independent)
 SET:x>20 category = "very large"
 SET:x>10 category = "large"
 SET:x>5 category = "medium"
 SET:x<=5 category = "small"
 WRITE category, !                 // medium

QUIT with Post-Conditional

 // Early exit from a FOR loop
 SET sum = 0
 FOR i = 1:1:100 {
     SET sum = sum + i
     QUIT:sum>100
 }
 WRITE "Sum: ", sum, " at i=", i, !
 // Output: Sum: 105 at i=14

 // In method: return early if precondition fails
 ClassMethod Divide(a As %Numeric, b As %Numeric) As %Numeric
 {
     QUIT:b=0 ""    // Return empty if division by zero
     QUIT a / b
 }

DO with Post-Conditional

 // Call a method only if condition is met
 DO:status'=$$$OK ..LogError(status)

 // Multiple conditional calls
 DO:age>=18 ..ProcessAdult(name)
 DO:age<18 ..ProcessMinor(name)

Post-Conditional with Complex Expressions

 // Parentheses for complex conditions
 SET:(x>0)&&(x<100) range = "valid"

 // String conditions
 SET name = "Alice"
 WRITE:name="Alice" "Found Alice!", !

 // Negation
 SET:(obj'="") result = obj.Calculate()

 // Post-conditional on KILL
 KILL:cleanup tempVar

Common Patterns

 // Default value assignment
 SET:('$DATA(config)) config = "default"

 // Conditional increment
 SET:isValid count = count + 1

 // Guard clause in a method
 ClassMethod Process(input As %String) As %Status
 {
     QUIT:input="" $$$ERROR($$$GeneralError, "Input required")
     // ... rest of processing
     QUIT $$$OK
 }

 // Conditional THROW
 THROW:status'=$$$OK ##class(%Exception.StatusException).CreateFromStatus(status)

2. Quit vs Return

Key Points

  • QUIT: Exits the current context -- loop body, method, or DO block
  • RETURN: Always exits the current method/routine, even from within a loop
  • QUIT in loop: Exits only the innermost loop, continues after the loop
  • RETURN in loop: Exits the entire method from within the loop
  • Return value: Both QUIT expr and RETURN expr can return values from methods
  • QUIT from method: When used outside a loop, QUIT exits the method (same as RETURN)

Detailed Notes

Overview

QUIT and RETURN both exit code, but they differ in what they exit. QUIT exits the nearest enclosing context (loop or method), while RETURN always exits the method. This distinction is critical when you need to exit a method from inside a nested loop.

QUIT in Loops vs Methods

 ClassMethod Example1()
 {
     WRITE "Before loop", !
     FOR i = 1:1:10 {
         QUIT:i=3         // Exits the FOR loop only
     }
     WRITE "After loop, i=", i, !    // Prints! i=3
     WRITE "Method continues", !      // Also prints
 }
 ClassMethod Example2()
 {
     WRITE "Before loop", !
     FOR i = 1:1:10 {
         RETURN:i=3       // Exits the ENTIRE METHOD
     }
     WRITE "After loop", !            // NEVER reached
 }

QUIT vs RETURN in Nested Loops

 ClassMethod NestedExample()
 {
     FOR i = 1:1:5 {
         FOR j = 1:1:5 {
             IF (i = 3) && (j = 2) {
                 QUIT    // Exits inner FOR loop only
                 // Outer loop continues with i=3, then i=4, i=5
             }
         }
         WRITE "i=", i, !    // Prints for all i values
     }
     WRITE "Done", !          // Prints
 }
 ClassMethod NestedReturn()
 {
     FOR i = 1:1:5 {
         FOR j = 1:1:5 {
             IF (i = 3) && (j = 2) {
                 RETURN   // Exits the ENTIRE method immediately
             }
         }
         WRITE "i=", i, !    // Prints for i=1, i=2 only
     }
     WRITE "Done", !          // NEVER reached
 }

Returning Values

 // QUIT with return value (in a method returning a value)
 ClassMethod Add(a, b) As %Integer
 {
     QUIT a + b
 }

 // RETURN with return value
 ClassMethod FindFirst(list As %ListOfDataTypes) As %String
 {
     FOR i = 1:1:list.Count() {
         SET item = list.GetAt(i)
         IF item [ "target" {
             RETURN item    // Exit method immediately with value
         }
     }
     RETURN ""              // Not found
 }

 // QUIT in a method with no return value
 ClassMethod Process()
 {
     QUIT:errorCondition    // Exit method (no return value)
     // ... processing
 }

QUIT in DO Blocks

 // QUIT exits a DO block (argumentless DO with code block)
 DO {
     SET x = $RANDOM(10)
     QUIT:x=5              // Exits the DO block, not any outer loop
     WRITE x, " "
 } WHILE x '= 5

Documentation References

3. Boolean evaluation

Key Points

  • True: Any nonzero numeric value is true (1, -1, 42, 3.14)
  • False: Zero (0) is false
  • Empty string: "" evaluates to 0, which is false
  • Non-numeric strings: "abc" evaluates to 0 (false) because its numeric value is 0
  • Leading numeric strings: "5abc" evaluates to 5, which is true
  • Canonical: Truthiness depends on the numeric interpretation of the value
  • Boolean result: Comparison and logical operators always return 0 or 1

Detailed Notes

Overview

ObjectScript does not have a dedicated boolean type. Instead, it uses numeric evaluation: zero is false, any nonzero value is true. When a string is evaluated as a boolean, it is first converted to its numeric value. Understanding this coercion is critical for writing correct conditions.

Truthiness Rules

 // Nonzero = true
 IF 1 WRITE "1 is true", !            // prints
 IF -1 WRITE "-1 is true", !          // prints
 IF 42 WRITE "42 is true", !          // prints
 IF 0.001 WRITE "0.001 is true", !    // prints

 // Zero = false
 IF 0 WRITE "0 is true", !            // does NOT print
 IF '0 WRITE "0 is false", !          // prints

 // Empty string = 0 = false
 IF "" WRITE "empty is true", !       // does NOT print
 SET x = ""
 IF 'x WRITE "empty is false", !      // prints

 // Non-numeric strings = 0 = false
 IF "abc" WRITE "abc is true", !      // does NOT print
 IF "hello" WRITE "hello is true", !  // does NOT print

 // Leading numeric strings = their numeric value
 IF "5abc" WRITE "5abc is true", !    // prints (5 is nonzero)
 IF "0abc" WRITE "0abc is true", !    // does NOT print (0)

Boolean Results from Comparisons

 // Comparisons return exactly 0 or 1
 WRITE 5 > 3, !             // 1
 WRITE 3 > 5, !             // 0
 WRITE "A" = "A", !         // 1
 WRITE "A" = "B", !         // 0

 // Can store boolean results
 SET isAdult = (age >= 18)
 SET isValid = (name '= "") && (age > 0)

 // Boolean arithmetic
 SET total = (a > 0) + (b > 0) + (c > 0)
 WRITE "Number of positive values: ", total, !

Common Boolean Pitfalls

 // PITFALL: Testing a string that should be boolean
 SET flag = "yes"
 IF flag WRITE "flag is true", !      // does NOT print!
 // "yes" has numeric value 0, so it is false in boolean context

 // Correct approach: test against specific values
 IF flag = "yes" WRITE "flag is yes", !  // prints

 // PITFALL: Empty string vs zero
 // In boolean context (IF), "" acts like 0 (false)
 // But "" and 0 are NOT equal with the = operator!
 SET val = ""
 IF val WRITE "truthy", !             // does NOT print ("" is falsy)
 IF val = 0 WRITE "equal to zero", !  // does NOT print! ("" '= 0)
 IF val = "" WRITE "equal to empty", ! // prints

 SET zero = 0
 IF zero WRITE "truthy", !            // does NOT print (0 is falsy)
 IF zero = "" WRITE "equal to empty", ! // does NOT print! (0 '= "")
 IF zero = 0 WRITE "equal to zero", ! // prints

 // To coerce "" to numeric before comparison, use unary +
 WRITE +"" = 0, !            // 1 (+"" gives 0, then 0 = 0)
 WRITE "" = 0, !             // 0 ("" is not 0)

 // PITFALL: Non-numeric strings are all falsy
 IF "yes" WRITE "yes", !     // does NOT print ("yes" = 0 numerically)
 IF "true" WRITE "true", !   // does NOT print
 IF "abc" WRITE "abc", !     // does NOT print
 WRITE "yes" = 0, !          // 0 ("yes" is not 0 -- different values)
 WRITE +"yes" = 0, !         // 1 (+"yes" coerces to 0, then 0 = 0)

NOT Operator

 SET x = 5
 WRITE 'x, !                // 0 (NOT 5 = false)

 SET y = 0
 WRITE 'y, !                // 1 (NOT 0 = true)

 // Double negation
 WRITE ''x, !               // 1 (NOT NOT 5 = true)
 WRITE ''y, !               // 0 (NOT NOT 0 = false)

 // '' can normalize any value to 0 or 1
 SET normalized = ''someValue
 // normalized is now exactly 0 or 1

Truthiness in Control Flow

 // IF uses truthiness
 SET name = "Alice"
 IF name WRITE "name has a value", !    // does NOT print ("Alice" = 0)

 // Correct: check for non-empty
 IF name '= "" WRITE "name is not empty", !  // prints

 // $DATA returns 0 (falsy) or 1/10/11 (truthy)
 IF $DATA(^config("key")) WRITE "key exists", !

 // $LENGTH returns 0 for empty, positive for non-empty
 IF $LENGTH(name) WRITE "name has length", !  // prints (5 > 0)

Exam Preparation Summary

Critical Concepts to Master:

  1. Post-conditional syntax: COMMAND:condition -- colon directly after command, no space
  2. QUIT vs RETURN: QUIT exits nearest context (loop or method); RETURN always exits the method
  3. QUIT in nested loops: Only exits the innermost loop -- use RETURN to exit the method
  4. Boolean evaluation: 0 = false, nonzero = true; "" is falsy; "abc" is falsy (numeric value 0)
  5. String truthiness trap: "yes", "true", "abc" are all FALSE because their numeric value is 0
  6. "" vs 0: Both are falsy in boolean context, but `"" = 0` is FALSE — they are different values. Use unary `+` to coerce strings to numbers before numeric comparison
  7. Post-conditionals on any command: SET, QUIT, DO, WRITE, KILL, THROW all support them
  8. Return values: Both QUIT expr and RETURN expr can return values from methods

Common Exam Scenarios:

  • Predicting whether QUIT exits a loop or method based on context
  • Determining the output of code with post-conditionals
  • Evaluating boolean expressions with string values (truthiness traps)
  • Choosing between QUIT and RETURN for a given flow control need
  • Writing guard clauses with QUIT:condition in methods
  • Understanding that "" and 0 are both falsy but `"" = 0` is false
  • Determining which lines execute after QUIT in a nested loop

Hands-On Practice Recommendations:

  • Write methods using post-conditionals instead of IF blocks and verify behavior
  • Create nested loops and test QUIT vs RETURN at different levels
  • Experiment with truthiness of various values: "", "abc", "0", "1", 0, 1, "5abc"
  • Test `"" = 0`, `0 = ""`, `+"" = 0` to understand coercion vs comparison
  • Write a method that uses QUIT:condition as a guard clause
  • Practice the pattern of QUIT:errorCondition $$$ERROR(...)
  • Test double negation ('') to normalize values to boolean 0/1

Report an Issue