Alterator

Alterator is the base trait for stages that wrap a single inner stage — the alterand. It declares the abstract alterand member. Resource disposal stays with the Evolutions returned by apply and skip: a concrete alterator must make sure its evolution delegates dispose to the alterand's evolution.

The type parameter S is covariant and bounded to Stage.Any, preserving the concrete type of the alterand in the wrapper's static type without information loss.

Do not mix Alterator with traits that supply the stage's own continuation — its skip, evolve or dispose, as SAMStage and Stagnation do. An alterator's evolution has to be built from the alterand's; a mixin that answers this instead — and both of those seal it that way — leaves the alterand neither evolved nor disposed, and nothing signals it.

Traits that only shape apply are safe to mix in, because they leave the continuation to the alterator itself. SafeStage and Fruitful are both used that way in this library, by Safe and Lift respectively.

The base package provides four type aliases for wrapping stages. The first two name the wrapper itself and are built on Alterator; the other two name the function that produces one:

Alias Meaning
UnaryOperator[+S, -I, +O, +E] Wraps any Stage[I, ?, ?]; may change all three type parameters
Decorator[-I, +O, +E] Wraps a Stage[I, O, E] and preserves its full type
Alteration[-IS, +OS] A function IS => OS that transforms one stage type into another
Decoration[I, O, E] An Alteration[Stage[I, O, E], Stage[I, O, E]] — the shape passed to Evolution.map

Decorator is the alias to reach for when building middleware that wraps a stage without changing its interface:

import h8io.stages.*
import h8io.stages.base.*
class Logged[I, O, E](val alterand: Stage[I, O, E]) extends Decorator[I, O, E] {
  override def apply(in: I): Yield[I, O, E] = {
    println(s"apply($in)")
    alterand(in)
  }

  override def skip(): Evolution[I, O, E] = alterand.skip()
}

val logged = new Logged(DoubleInt)
// logged: Logged[Int, Int, Nothing] = <function1>
logged(10)
// apply(10)
// res0: Yield[Int, Int, Nothing] = Some(
//   out = 20,
//   status = Success,
//   evolution = <function1>
// )
logged.skip()
// res1: Evolution[Int, Int, Nothing] = <function1>

Alteration and Decoration are the shapes used when passing stage transformations as values — for example, to Evolution.map.