Side Effects¶
Observing with .tap()¶
Peek at values mid-pipeline without modifying them:
import etl4s._
val pipeline = Extract("hello world")
.tap(x => println(s"Got: $x"))
~> Transform[String, Array[String]](_.split(" "))
pipeline.unsafeRun(())
// prints: Got: hello world
// returns: Array("hello", "world")
Chain taps at different stages:
val pipeline = fetchData
.tap(files => println(s"Fetched ${files.size} files"))
~> processFiles
.tap(count => println(s"Processed $count files"))
Sequential Effects with >>¶
Run multiple nodes in order, same input to each. Only the last result is kept:
val logStart = Node[String, Unit](s => println(s"Start: $s"))
val logEnd = Node[String, Unit](s => println(s"End: $s"))
val process = Node[String, Int](_.length)
val pipeline = logStart >> logEnd >> process
pipeline.unsafeRun("hello")
// prints: Start: hello
// prints: End: hello
// returns: 5
Common for setup/teardown:
val clearCache = Node { println("Clearing cache...") }
val warmCache = Node { println("Warming cache...") }
val pipeline = clearCache >> warmCache >> mainPipeline
Or audit logging:
val auditStart = Node[Request, Unit](r => log(s"Started ${r.id}"))
val auditEnd = Node[Request, Unit](r => log(s"Finished ${r.id}"))
val pipeline = auditStart >> processRequest >> auditEnd
Laziness¶
Side effects are lazy - nothing executes until .unsafeRun(). Build and compose freely without triggering effects.