Skip to main content
Version: 2.x

Effectual Optics

In addition to "pure" optics like the ones discussed so far ZIO Optics supports optics where getting and setting involve ZIO or STM effects. This allows using optics to work with ZIO data structures such as TMap.

These optics work exactly the same way as pure optics except that instead of the getter and setter returning an Either they return a ZIO or an STM value.

import zio._

trait Optic[-GetWhole, -SetWholeBefore, -SetPiece, +GetError, +SetError, +GetPiece, +SetWholeAfter] {
def getOptic(whole: GetWhole): IO[GetError, GetPiece]
def setOptic(piece: SetPiece)(whole: SetWholeBefore): IO[SetError, SetWholeAfter]
}

To work with these optics, import zio.optics.opticsm._ for optics where the result type is a ZIO value or zio.optics.toptics._ for optics where the result type is an STM value. This will automatically bring the appropriate version of the Optic data type as well as its constructors and syntax into scope.

For example, here is how we could define an optic to work with a nested value inside a TMap:

import zio.stm._
import zio.optics.toptics._

val optic: Optional[TMap[String, Either[String, Int]], Int] =
TOptics.key("key") >>> Optic.right

Note that optics specific to STM data structures are defined in the TOptics object. Otherwise everything works the same way.

Just like with Ref, ZIO Optics provides special support for working with STM data types such as TMap, so we can update our TMap using "dot" syntax like this:

tMap.key("key").right.at(0).update(_ + 1)