R type _io[R] = |=[IO, R] type _errorOr[R] = ErrorOr |= R def program[R :_io :_option :_errorOr] (userId: String, teamName: String): Eff[R, Response] = for { userOpt <- fromIO(findUser(userId)) user <- fromOption(userOpt) team <- fromEither(createTeam(userId, teamName)) _ <- fromIO(storeTeam(team)) } yield createResponse(user, team) MTL Eff type Stack = Fx.fx3[Option, ErrorOr, IO] program[Stack](userId, teamName) .runOption // Eff[Fx2[Either, IO], Option[Response]] .runEither[Error] // Eff[Fx1[IO], Either[Error, Option[Response]] .to[IO] // IO[Either[Error, Option[Response]]] program(userId, teamName) // OptionT[EitherIO, Response] .value // EitherT[IO, Error, Option[Response]] .value // IO[Either[Error, Option[Response]]] type EitherIO[A] = EitherT[IO, Error, A] def program(userId: String, teamName: String): OptionT[EitherIO, Response] = { for { user <- OptionT[EitherIO, User] (EitherT.liftF[IO, Error, Option[User]](findUser(userId))) team <- OptionT.liftF[EitherIO, Team] (EitherT.fromEither[IO](createTeam(userId, teamName))) _ <- OptionT.liftF[EitherIO, Unit] (EitherT.liftF[IO, Error, Unit](storeTeam(team))) } yield createResponse(user, team) } MTL vs atnos-eff 41