scalus.uplc.transform
Members list
Type members
Classlikes
Replace nested Apply with Case/Constr
Replace nested Apply with Case/Constr
For example, replace (apply (apply (apply f a) b) c) with (case (constr 0 [a, b, c]) f). This is more memory/cpu efficient than nested Apply at least in Plutus V3 Plomin HF, protocol version 10.
With current machine costs, Apply costs 100 memory and 16000 cpu, same for Case/Constr. Hence (case (constr 0 [a, b, c]) f) costs 200 memory and 32000 cpu, while (apply (apply (apply f a) b) c) costs 300 memory and 48000 cpu.
Attributes
- Companion
- object
- Supertypes
Attributes
- Companion
- class
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
CaseConstrApply.type
Performs eta-reduction on a term.
Performs eta-reduction on a term.
Eta-reduction is the process of removing redundant lambda abstractions from a term. For example, the term λx. f x can be eta-reduced to f but only if
xis not free inffis a pure expression
Purity checking is handled by TermAnalysis.isPure. A term is pure if it does not contain any side effects, such as Error, Force of non-delayed terms, or saturated builtin applications. See TermAnalysis.isPure for comprehensive documentation on purity semantics.
Attributes
- See also
-
TermAnalysis.isPure for purity semantics
- Companion
- object
- Supertypes
Extract forced builtins to top level
Extract forced builtins to top level
For example, replace (force (force (builtin fstPair))) with (lam builtin_FstPair (builtin_FstPair (pair true false)) (! (! __builtin_FstPair))). This is more memory/cpu efficient than nested Force at least in Plutus V3 Plomin HF, protocol version 10.
With current machine costs, Force costs 100 memory and 16000 cpu, same for Builtin. Hence (lam builtin_FstPair (builtin_FstPair (pair true false)) (! (! __builtin_FstPair))) costs 200 memory and 32000 cpu, while (force (force (builtin fstPair))) costs 300 memory and 48000 cpu.
Attributes
- Companion
- object
- Supertypes
Attributes
- Companion
- class
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
Optimizer that performs function inlining, beta-reduction, and dead code elimination.
Optimizer that performs function inlining, beta-reduction, and dead code elimination.
The Inliner performs several transformations:
- '''Beta-reduction''': Replaces function application with direct substitution when safe
- '''Identity function inlining''': Eliminates identity functions like
λx.x - '''Dead code elimination''': Removes unused lambda parameters when the argument is pure
- '''Small value inlining''': Inlines variables, small constants, and builtins
- '''Force/Delay elimination''': Simplifies
Force(Delay(t))tot
==Inlining Strategy==
The inliner uses occurrence counting and purity analysis to decide what is safe to inline:
- Variables, builtins, and small constants (≤64 bits) can be duplicated safely
- Larger values are only inlined if used once
- Pure unused arguments are eliminated entirely
==Example==
// Input: (λx. λy. x) 42 100
// After inlining identity and dead code elimination:
// Output: 42
val inliner = new Inliner()
val optimized = inliner(term)
// Check what was optimized
println(inliner.logs.mkString("\n"))
==Implementation Details==
The inliner performs capture-avoiding substitution to prevent variable capture during beta-reduction. It maintains an environment of inlined bindings and recursively processes the term tree.
Value parameters
- logger
-
Logger for tracking inlining operations (defaults to new Log())
Attributes
- See also
-
TermAnalysis.isPure for purity analysis used in dead code elimination
Optimizer for the base optimizer trait
- Companion
- object
- Supertypes
No-op optimizer that returns the term unchanged without any optimizations.
No-op optimizer that returns the term unchanged without any optimizations.
This is useful as a default when optimization is disabled or as a placeholder in testing.
Attributes
- Supertypes
- Self type
-
NoopOptimizer.type
Base trait for UPLC term optimizers.
Base trait for UPLC term optimizers.
An optimizer transforms a UPLC (Untyped Plutus Core) term into an equivalent but more efficient term. Optimizers can perform various transformations such as:
- Dead code elimination
- Function inlining and beta-reduction
- Eta-reduction (removing redundant lambda abstractions)
- Constant folding
- Conversion of lazy evaluation to strict evaluation when safe
==Usage==
Optimizers are typically used in optimization pipelines (see V1V2Optimizer and V3Optimizer) where multiple optimization passes are applied sequentially:
val optimizer = new StrictIf()
val optimizedTerm = optimizer(term)
println(s"Optimizations applied: ${optimizer.logs.mkString(", ")}")
==Implementation Notes==
Optimizer implementations should:
- Accept a scalus.uplc.eval.Logger as constructor parameter for logging optimizations
- Be deterministic: applying the same optimizer to the same term should always produce the same result
- Preserve semantics: the optimized term must be equivalent to the original term
- Log all applied optimizations for debugging and analysis
Attributes
- See also
-
StrictIf for lazy-to-strict if-then-else conversion
EtaReduce for eta-reduction optimization
Inliner for function inlining and dead code elimination
ForcedBuiltinsExtractor for extracting forced builtins to top level
CaseConstrApply for optimizing nested Apply with Case/Constr (Plutus V3)
- Supertypes
-
class Objecttrait Matchableclass Any
- Known subtypes
-
class CaseConstrApplyclass EtaReduceclass ForcedBuiltinsExtractorclass Inlinerobject NoopOptimizerclass StrictIfclass V1V2Optimizerclass V3OptimizerShow all
Optimizes if-then-else expressions by converting lazy branches to strict evaluation when safe.
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
Delaynodes to suspend branch evaluation - 1
Forcenode 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
- Companion
- object
- Supertypes
Static analysis utilities for UPLC terms.
Static analysis utilities for UPLC terms.
Provides analysis methods for determining properties of UPLC terms that are useful for optimization and transformation passes.
Attributes
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
TermAnalysis.type
Attributes
- Supertypes
Attributes
- Supertypes