# Failure Most programming languages treat control flow as a matter of true or false, yes or no, one or zero. They evaluate boolean conditions and branch accordingly, creating a world of binary decisions that often requires checking conditions twice - once to see if something is possible, and again to actually do it. Verse takes a different approach. Instead of asking "is this true?", Verse asks "does this succeed?" This distinction might seem subtle, but it changes how programs are written and reasoned about. Failure isn't an error or an exception-it's a first-class concept that drives control flow. When an expression fails, it doesn't crash your program or throw an exception that needs to be caught. Instead, failure is a normal, expected outcome that your code handles gracefully through the structure of the language itself. Consider the simple act of accessing an array element. In traditional languages, you might write: ```verse if (Index < Array.Length) { # Traditional, non-Verse Value = Array[Index] Process(Value) } ``` This checks validity separately from access, creating opportunities for bugs if the check and access become separated or if the array changes between them. In Verse, validation and access are unified: ```verse if (Value := Array[Index]): Process(Value) ``` The array access either succeeds and binds the value, or it fails and execution moves on. There is no separate validation step, so the check and access cannot become inconsistent, and no undefined behavior from accessing invalid indices. ## Failable Expressions A failable expression is one that can either succeed and produce a value, or fail and produce nothing. This isn't the same as returning null or an error code - when an expression fails, it literally produces no value at all. The computation stops at that point in that particular path of execution. Many operations are naturally failable. Array indexing fails when the index is out of bounds. Map lookups fail when the key doesn't exist. Comparisons fail when the values aren't equal. Division fails when dividing by zero. Even simple literals can be made to fail: ```verse 42 # Always succeeds with value 42 false? # Always fails - the query of false true? # Always succeeds - the query of true ``` The query operator `?` turns any value into a failable expression. When applied to `false`, it always fails. When applied to any other value, it succeeds with that value. This simple mechanism provides immense power for controlling program flow. You can create your own failable expressions through functions marked with the `` effect: ```verse ValidateAge(Age:int):int = Age >= 0 # Fails if age is negative Age <= 150 # Fails if age is unrealistic Age # Returns the age if both checks pass ``` This function doesn't just check conditions - it embodies them. If the age is invalid, the function fails. If it's valid, it succeeds with the age value. The validation and the value are inseparable. ## Failure Contexts Not every part of a program can execute failable expressions. They can only appear in failure contexts--places where the language knows how to handle both success and failure. Each failure context defines what happens when expressions within it fail. The most common failure context is the condition of an `if` expression: ```verse if (Player := GetPlayerByName[Name], Score := GetPlayerScore[Player], Score > 100): Print("High scorer: {Name} with {Score} points!") ``` This `if` condition contains three potentially failable expressions. All must succeed for the body to execute. If any fails, the entire condition fails, and control moves to the `else` branch (if present) or past the `if` entirely. The beauty is that each expression can use the results of previous ones - `Score` is only computed if we successfully found the `Player`. The `for` expression creates a failure context for each iteration of the domain clause: ```verse for (Item : Inventory, IsWeapon[Item], Damage := GetDamage[Item], Damage > 50): Print("Powerful weapon: {Item} with {Damage} damage") ``` Each iteration attempts the failable expressions. If they all succeed, the body executes for that item. If any fails, that iteration is skipped, and the loop continues with the next item. This creates a natural filtering mechanism without explicit conditional logic. !!! note "Unreleased Feature" `first` has not yet been released. The following documents planned functionality that is not currently available. Similar to `for`, the `first` expression creates a failure context for the domain clause: ```verse PowerfulWeapon := option. first(Item : Inventory, IsWeapon[Item], Damage := GetDamage[Item], Damage > 50). Item ``` Unlike `for`, if there are no successful iterations, `first` itself will fail, and so must be used in a failure context. In the above example, `option` is used to handle failure of the `first`. Functions marked with `` create a failure context for their entire body: ```verse FindBestWeapon(Inventory:[]item):item = var BestWeapon:?item = false var MaxDamage:int = 0 for (Item : Inventory, IsWeapon[Item], Damage := GetDamage[Item]): if (Damage > MaxDamage): set BestWeapon = option{Item} set MaxDamage = Damage BestWeapon? # Fails if no weapon was found ``` The function body is a failure context, allowing failable expressions throughout. The final line extracts the value from the option, failing if no weapon was found. ## Speculative Execution When you execute code in a failure context, changes to mutable variables are provisional—they only become permanent if the entire context succeeds. Functions that modify state in failure contexts must use the `` or the `` effect specifier (see [Effects](13_effects.md)): ```verse m:=module: buyer := class: var PlayerGold:int AttemptPurchase(Cost:int):void = set PlayerGold = PlayerGold - Cost # Provisional change PlayerGold >= 0 # Check if still valid # If this fails, PlayerGold reverts to original value ``` If the check fails, the subtraction is automatically rolled back. You don't need to manually restore the original value or check conditions before modifying state. This transactional behavior makes complex state updates safe and predictable. Either everything succeeds and all changes are committed, or something fails and nothing changes. ```verse game := class: var State:game_state ComplexOperation():void = ModifyHealth() # All these operations UpdateInventory() # are provisional ChargeResources() # until all succeed ValidateFinalState[] # If this fails, everything rolls back ``` The `game` class has multiple methods that update the `game_state`, before returning `ComplexOperation` validates that the object is in a valid state, if it is not, all changes performed in the method are rolled back. ## The Logic of Failure Verse provides logical operators that work with failure, creating an algebra for combining failable expressions. The `and` operator ensures that both expression succeed. The `not` operator inverts success and failure: ```verse if (not (Enemy := GetNearestEnemy[]) and Score > 0): Print("Coast is clear!") # Executes when GetNearestEnemy fails ``` It is noteworthy that `Enemy` is not in scope within the `then` branch because it is under a `not`. The `or` operator provides alternatives: ```verse Weapon := PrimaryWeapon[] or SecondaryWeapon[] or DefaultWeapon? ``` This tries each option in order, stopping at the first success. It's not evaluating boolean conditions - it's attempting computations and taking the first one that succeeds. You can combine these operators to create sophisticated control flow: ```verse ValidatePlayer(Player:player):void = IsAlive[Player] not IsStunned[Player] HasAmmunition[Player] or HasMeleeWeapon[Player] ``` This function succeeds only if the player is alive, not stunned, and has either ammunition or a melee weapon. Each line is a separate failable expression that must succeed. Another interesting use case is `not not Exp` -- it succeeds if `Exp` succeeds but all effects of `Exp` are thrown away. This is a way to try to see if a complex operation would succeed. ## Expressions in Decides A subtle feature is how relational expressions behave in decides contexts. When a comparison appears in a context that can handle failure, it doesn't just test a condition—it produces a value, specifically it returns its left-hand side. So `X>0` returns `X` and `0<=X` returns `0`. This behavior applies to all comparison operators in decides contexts: ```verse GetIfNotEqual(X:int, Y:int):int = X <> Y # Returns X when X ≠ Y, fails when X = Y GetIfLessOrEqual(X:int, Limit:int):int = X <= Limit # Returns X when X ≤ Limit, fails otherwise GetIfGreaterThan(X:int, Threshold:int):int = X > Threshold # Returns X when X > Threshold, fails otherwise ``` Comparison expressions of the form `A op B` return `A` when the comparison succeeds, and fail when the comparison is false. This creates concise validation functions that either return `Value` or fail: ```verse ValidateInRange(Value:int, LwrBnd:int, UprBnd:int):int = Value >= LwrBnd and Value <= UprBnd ``` ## Option Types The option type and failure are intimately connected. An option either contains a value or is empty (represented by `false`). The query operator `?` converts between options and failure: ```verse M():void= MaybeValue:?int = option{42} # An optional int Value := MaybeValue? # Succeeds with 42 Empty:?int = false # An empty value Other := Empty? # Failure ``` The `option{}` constructor works in reverse, converting failure to an empty option: ```verse Result := option{RiskyComputation[]} # option{value} if computation succeeds # otherwise false ``` This bidirectional conversion makes options and failure interchangeable, allowing you to choose the most appropriate representation for your specific use case. The option type `?T` represents values that may or may not be present. The question mark appears *before* the type, not after: ```verse ValidSyntax:?int = option{42} # Correct ``` The `?` prefix applies to any type: ```verse MaybeNumber:?int = option{42} MaybeText:?string = option{"hello"} MaybePlayer:?player = option{player{}} ``` Use the `option{}` constructor to wrap a value: ```verse Filled:?int = option{42} Empty:?int = false Result:?int = option{RiskyComputation[]} # false if computation fails ``` Empty options and `false` are equivalent—an empty option *is* `false`: ```verse EmptyOption:?int = false EmptyOption = false # This comparison succeeds ``` Verse has a rich and flexible syntax which can also sometimes cause subtle bugs. A comma gives rise to a tuple in an `option` whereas a semicolon evaluates all values but retain only the last one: ```verse # Comma creates tuple option{1, 2}? = (1, 2) # Semicolon creates sequence - last value is used option{1; 2}? = 2 ``` ### Unwrapping The query operator `?` extracts values from options, failing if the option is empty: ```verse M():void= MaybeValue:?int = option{42} Value := MaybeValue? # Succeeds with 42 Empty:?int = false Other := Empty? # Fails - cannot unwrap empty option ``` Unwrapping is only allowed in failure contexts: ```verse # Valid: In if condition (failure context) if (Value := MaybeInt?): Print("Got {Value}") # Valid: In for loop (failure context) for (Item : Items, ValidItem := ProcessItem(Item)?): UseItem(Item) # Valid: In function body (failure context) GetRequired(Maybe:?int):int = Maybe? # Fails if Maybe is empty ``` ### Nesting Options can be nested to represent multiple layers of absence: ```verse # Double-nested option Double:??int = option{option{42}} # Single unwrap gets outer option if (Inner := Double?): if (TheValue := Inner?): # TheValue has type int, equals 42 # Double unwrap gets the value directly Value := Double?? # Fails if either layer is empty ``` Helper functions also work with nested options: ```verse UnpackNested(MaybeValue:??int):?int = if (Inner := MaybeValue?): Inner else: option{-1} # Default for outer empty DirectUnpack(MaybeValue:??int):int = if (Value := MaybeValue??): Value else: -1 # Default for any level empty ``` ### Chained Access The `?.` operator provides safe member access on optional values: ```verse entity := class: Name:string = "Unknown" Health:int = 100 MaybeEntity:?entity = option{entity{}} # Safe field access if (Name := MaybeEntity?.Name): Print("Entity: {Name}") # Succeeds # Safe method call MaybeEntity?.TakeDamage(10) # Only calls if entity present # Chaining through multiple optionals linked_list := class: Value:int = 0 Next:?linked_list = false Head:?linked_list = option{linked_list{Value := 1}} SecondValue := Head?.Next?.Value # Fails if any link is empty ``` The `?.` operator short-circuits—if the option is empty, the entire expression fails without evaluating the member access. ### Defaulting Use the `or` operator to provide fallback values for empty options: ```verse MaybeValue:?int = false Value := MaybeValue? or 42 # Yields 42 # Chaining multiple options Primary:?string = false Secondary:?string = option{"backup"} Default:string = "default" Result := Primary? or Secondary? or Default ``` ### Comparison Empty options equal `false`, and filled options equal their unwrapped values when compared properly: ```verse EmptyOption:?int = false EmptyOption = false # Succeeds FilledOption:?int = option{1} FilledOption? = 1 # Succeeds - unwrap then compare ``` However, you cannot directly compare optional and non-optional values without unwrapping: ```verse Opt:?int = option{42} Regular:int = 42 # Must unwrap to compare if (Opt? = Regular): Print("Equal") ``` ## Failure with Optionals Combining decides functions with optional return types, creates a system with multiple layers of failure. This pattern enables expressing complex conditions concisely while maintaining clarity. A function can fail at two levels: - *Function-level failure*: The entire function fails using `` - *Value-level failure*: The function succeeds but returns an empty option ```verse FindEligiblePlayer(Name:string):?player = Name <> "" # Layer 1: Fail if name is empty Player := LookupPlayer[Name] # Layer 1: Fail if player not found option{IsActive[Player]} # Layer 2: Empty option if player inactive ``` This function has three possible outcomes: - *Function fails*: Empty name or player not found - *Function succeeds with empty option*: Player found but inactive - *Function succeeds with filled option*: Player found and active Calling this function demonstrates the layered failure: ```verse # Function-level failure Result1 := FindEligiblePlayer[""] # Fails, Result1 never assigned # Function succeeds, returns empty option if (Player := FindEligiblePlayer["InactiveUser"]?): # Won't execute - function succeeds but ? query fails else: # Executes here # Function succeeds, returns filled option if (Player := FindEligiblePlayer["ActiveUser"]?): # Executes with Player bound to the active player ``` This pattern is particularly powerful for validation with different failure modes: ```verse ValidateScore(Score:int):?int = Score >= 0 # Layer 1: Reject negative scores (invalid input) option{Score <= 100} # Layer 2: Reject high scores (out of range) ``` The distinction between function-level and value-level failure lets you express different kinds of errors. Function-level failure typically means "this operation couldn't complete" while value-level failure means "the operation completed but the result doesn't meet the expected criteria." ## Casts as Decides Type casting in Verse is integrated into the failure system. A dynamic cast behaves just like a `` function call and similarly uses bracket syntax. For example `Type[value]` attempts to cast `value`'s type to `Type` and fails if unsuccessful. This is also works with user defined types which must specify ``: ```verse component := class: Name:string = "Component" physics_component := class(component): Velocity:float = 0.0 # Casting as a decides operation TryGetPhysics(Comp:component):physics_component = physics_component[Comp] # Succeeds if Comp is actually a physics_component ``` This makes type-based dispatch easily expressible: ```verse ProcessComponent(Comp:component):void = if (Physics := physics_component[Comp]): UpdatePhysics(Physics) else if (Render := render_component[Comp]): UpdateRendering(Render) else: # Unknown component type UpdateGeneric(Comp) ``` The cast itself is the condition—no separate type checking needed. When the cast succeeds, you have both confirmed the type and obtained a properly-typed reference. You can chain casts with other decides operations: ```verse GetActivePhysicsComponent(Entity:entity):physics_component = Comp := Entity.GetComponent[] # Fails if no component Physics := physics_component[Comp] # Fails if not physics IsActive[Physics] # Fails if inactive Physics ``` Each step must succeed for the function to return a value. This creates self-documenting validation chains where type requirements are explicit. Casts work with the `or` combinator for fallback types: ```verse GetInteractable(Entity:entity):component = physics_component[Entity] or trigger_component[Entity] or scripted_component[Entity] ``` This tries each cast in order, returning the first successful one. It's type-safe because all options share the common `component` base type. ## Idioms and Patterns As you work with failure, certain patterns emerge that solve common problems elegantly. The validation chain pattern uses sequential failures to ensure all conditions are met: ```verse ProcessAction(Action:action):void = Player := GetActingPlayer[Action] IsValidTurn[Player] HasRequiredResources[Player, Action] Location := GetTargetLocation[Action] IsValidLocation[Location] ExecuteAction[Action] ``` Each line must succeed for execution to continue. This creates self-documenting code where preconditions are explicit and checked in order. The first-success pattern tries alternatives until one works: ```verse FindPath(Start:location, End:location):path = DirectPath[Start, End] or PathAroundObstacles[Start, End] or ComplexPathfinding[Start, End] ``` This naturally expresses trying simple solutions before complex ones. The filtering pattern uses failure to select items: ```verse GetEliteEnemies(Enemies:[]enemy):[]enemy = for (Enemy : Enemies, Level := GetLevel[Enemy], Level >= 10): Enemy ``` Only enemies that have a level and whose level is at least 10 are included in the result. The transaction pattern groups related changes: ```verse TradeItems(PlayerA:player, PlayerB:player, ItemA:item, ItemB:item):void = RemoveItem[PlayerA, ItemA] RemoveItem[PlayerB, ItemB] AddItem(PlayerA, ItemB) AddItem(PlayerB, ItemA) ValidateTrade[PlayerA, PlayerB] ``` Either the entire trade succeeds, or nothing changes. **Optional Indexing** When working with optional containers, you can access their contents using specialized query syntax that combines optional checking with element access. Optional tuples support direct element access through the query operator: ```verse MaybePair:?tuple(int, string) = option{(42, "answer")} # Access first element if (FirstValue := MaybePair?(0)): # FirstValue is 42 (type: int) Print("First: {FirstValue}") # Access second element if (SecondValue := MaybePair?(1)): # SecondValue is "answer" (type: string) Print("Second: {SecondValue}") ``` The syntax `Option?(index)` simultaneously: - Queries whether the option is non-empty - Accesses the tuple element at the given index - Binds the element value if both succeed **Composition and Call Chains** Decides functions compose naturally, allowing complex operations to be built from simple, reusable pieces. When a decides function calls another decides function, failures propagate automatically. ```verse ValidatePositive(X:int):int = X > 0 Double(X:int):int = Validated := ValidatePositive[X] # Fails if X ≤ 0 Validated * 2 ``` If `ValidatePositive` fails, `Double` fails immediately. The validated value flows through the chain. **Preserving failure context:** When calling decides functions in non-decides contexts, you must handle failure explicitly: ```verse # This won't compile - ProcessPlayer doesn't have # BadProcessPlayer(Name:string):void = # Player := FindPlayer[Name] # ERROR: Unhandled failure # Handle with if ProcessPlayerWithIf(Name:string):void = if (Player := FindPlayer[Name]): UsePlayer(Player) # Handle with or ProcessPlayerWithOr(Name:string):void = Player := FindPlayer[Name] or GetDefaultPlayer() UsePlayer(Player) ``` Understanding composition helps you build complex validation logic from simple, testable pieces. ## Runtime Errors While failure (``) represents normal control flow with transactional rollback, *runtime errors* represent unrecoverable conditions that terminate execution. Runtime errors propagate up the call stack, bypassing normal failure handling, and cannot be caught or recovered within Verse code. The `Err()` function explicitly triggers a runtime error with an optional message: ```verse ValidateInput(Value:int):int = if (Value < 0): Err("Negative values not allowed") Value ``` When a runtime error occurs, execution unwinds through the call stack, terminating the current operation: ```verse DeepFunction():int = Log("C") Err("Fatal error") # Runtime error here Log("D") # Never executes return 1 MiddleFunction():int = Log("B") Result := DeepFunction() # Error propagates through here Log("E") # Never executes return Result TopFunction():void = Log("A") Value := MiddleFunction() # Error propagates to here Log("F") # Never executes # Execution order: A, B, C, then terminates # Output: "ABC" ``` The runtime error propagates immediately, bypassing all subsequent code in the call chain. Runtime errors propagate through asynchronous operations, terminating spawned tasks: ```verse AsyncOperation():int = Log("Start") WaitTicks(1) Err("Async error") # Runtime error during async execution WaitTicks(1) # Never executes return 1 KickOff():void= # Error propagates out of spawned task spawn{ AsyncOperation() } ``` When a spawned task encounters a runtime error, that specific task terminates. The runtime error does not automatically propagate to the spawning context. ## Living with Failure Verse's approach to failure has roots in logic programming, where computations search for solutions rather than executing steps. When a path fails, the computation backtracks and tries alternatives. This non-deterministic model, while powerful, can be hard to reason about in its full generality. Verse tames this power by making failure contexts explicit and limiting backtracking to specific constructs. You get the benefits of logic programming - declarative code, automatic search, elegant handling of alternatives - without the complexity of full unification and unbounded backtracking. Consider a simple logic puzzle solver: ```verse SolvePuzzle(Constraints:[]constraint):solution = var State:solution = InitialState() for (Constraint : Constraints): ApplyConstraint(State, Constraint) ValidateSolution[State] State ``` If any constraint can't be satisfied, the entire attempt fails. In a full logic programming language, this might trigger complex backtracking. In Verse, the failure model is simpler and more predictable while still being expressive enough for most problems. Working effectively with failure in Verse requires a shift in mindset. Instead of thinking about error conditions that need to be avoided, think about success conditions that need to be met. Instead of defensive programming that checks everything before acting, write optimistic code that attempts operations and handles failure gracefully. This perspective makes code more readable and intent more clear. When you see a function marked with ``, you know it represents a computation that might not have a result. When you see expressions in sequence within a failure context, you know they represent conditions that must all be met. When you see the `or` operator, you know it represents alternatives to try. Failure in Verse isn't something to be feared or avoided - it's a tool to be embraced. It makes programs safer by eliminating certain categories of bugs. It makes code clearer by unifying validation and action. It makes complex operations simpler by providing automatic rollback. Most importantly, it aligns the way we write programs with the way we think about actions and decisions in the real world. As you write more Verse code, you'll find that failure becomes second nature. You'll reach for failable expressions naturally when expressing conditions. You'll structure your functions to fail early when preconditions aren't met. You'll compose failures to create sophisticated control flow without nested conditionals. And you'll appreciate how this different way of thinking about control flow leads to code that is both more robust and more expressive than traditional approaches.