StrictIf

scalus.uplc.transform.StrictIf
object StrictIf

Optimizes if-then-else expressions by converting lazy branches to strict evaluation when safe.

==Background==

UPLC (Untyped Plutus Core) uses strict evaluation: all function arguments are evaluated before the function is applied. However, for conditional expressions using IfThenElse, we need lazy evaluation to avoid evaluating both branches.

The standard compilation generates:

Force(Apply(Apply(Apply(Force(Builtin(IfThenElse)), condition), Delay(thenBranch)), Delay(elseBranch)))

This pattern has a cost:

  • 2 Delay nodes to suspend branch evaluation
  • 1 Force node to evaluate the selected branch
  • Total: '''3 extra term node evaluations per if-then-else'''

==The Optimization==

When both branches are "simple values" that evaluate to exactly 1 term, we can safely evaluate them strictly, removing the Delay/Force overhead:

Apply(Apply(Apply(Force(Builtin(IfThenElse)), condition), thenBranch), elseBranch)

This saves 3 term node evaluations, improving performance without changing semantics.

==Simple Values (Single-Term Evaluation)==

A term is considered "simple" if it evaluates to exactly 1 term:

  • Var: Variable lookup (1 term)
  • Const: Constant value (1 term)
  • LamAbs: Lambda abstraction - not executed until applied (1 term)
  • Builtin: Builtin function reference - not executed until applied (1 term)
  • Delay: Suspended computation - not executed (1 term)
  • Constr(_, Nil): Empty constructor (1 term)

Terms that are NOT simple (multi-term evaluation):

  • Apply: Requires evaluating function + argument (3+ terms)
  • Force: Requires evaluating the forced term (2+ terms)
  • Case: Requires evaluating scrutinee + pattern matching (2+ terms)
  • Constr(_, args) with args: Requires evaluating each argument (1 + n terms)
  • Error: Will fail immediately

==Example==

// Original: if condition then 42 else 100
Force(Apply(Apply(Apply(Force(Builtin(IfThenElse)), condition), Delay(Const(42))), Delay(Const(100))))
// Cost: 5 term evaluations (Force + Apply + Apply + Apply + Force + 2 Delay + selected Const)

// Optimized: both branches are constants (simple values)
Apply(Apply(Apply(Force(Builtin(IfThenElse)), condition), Const(42)), Const(100))
// Cost: 2 term evaluations (Force + Apply + Apply + Apply + selected Const)
// Savings: 3 term evaluations (2 Delay + 1 Force)

Attributes

See also

scalus.uplc.transform.EtaReduce for another UPLC optimization

scalus.uplc.transform.Inliner for beta-reduction and dead code elimination

Graph
Supertypes
class Object
trait Matchable
class Any
Self type
StrictIf.type

Members list

Value members

Concrete methods

def apply(term: Term): Term

Applies the strict if-then-else optimization without logging.

Applies the strict if-then-else optimization without logging.

Value parameters

term

The UPLC term to optimize

Attributes

Returns

The optimized term

def apply(term: Term, logger: String => Unit): Term

Applies the strict if-then-else optimization with logging.

Applies the strict if-then-else optimization with logging.

Value parameters

logger

Function to log optimization events

term

The UPLC term to optimize

Attributes

Returns

The optimized term

def strictIf(term: Term, logger: String => Unit): Term

Performs the strict if-then-else optimization by traversing the term tree.

Performs the strict if-then-else optimization by traversing the term tree.

Value parameters

logger

Function to log optimization events

term

The UPLC term to optimize

Attributes

Returns

The optimized term