Type Parameter Order
The most important difference between ZIO and Effect is the type parameter order.
// ZIO: Environment first
type ZIO[-R, +E, +A]
// R: Requirements (environment)
// E: Error (can fail with)
// A: Success (produces)
val example: ZIO[Console, IOException, String] =
ZIO.succeed("Hello")ZIO puts Environment (R) first
// Effect: Success first
type Effect<A, E, R>
// A: Success (produces)
// E: Error (can fail with)
// R: Requirements (context)
const example: Effect<string, IOException, Console> =
Effect.succeed("Hello")Effect puts Success (A) first
Why This Difference?
ZIO's order (R, E, A) groups the input (R) separately from outputs (E, A).
Effect's order (A, E, R) prioritizes the most common variation — the success type.
In practice, you write the success type most often. Effect's order makes partial type application feel more natural.
Mental Model
Think of Effect<A, E, R> as a function that requires context R and produces either E (error) or A (success):
// Conceptually
type Effect<A, E, R> = (context: Context<R>) => Promise<E | A>
Type Aliases
ZIO provides type aliases for common cases. Effect uses explicit type parameters instead.
// ZIO type aliases
type UIO[A] = ZIO[Any, Nothing, A]
type IO[E, A] = ZIO[Any, E, A]
type Task[A] = ZIO[Any, Throwable, A]
type RIO[R, A] = ZIO[R, Throwable, A]
val program: Task[String] =
ZIO.succeed("Hello")ZIO: UIO, IO, Task, RIO aliases
// Effect: explicit parameters
// No type aliases needed
const program: Effect<
string, // A
Throwable, // E
never // R
> = Effect.succeed("Hello")Effect: explicit parameters only
Reading Type Signatures
When reading Effect types, remember: A, E, R (Success, Error, Requirements).
// ZIO[-R, +E, +A]
def readFile(path: String): ZIO[Any, IOException, String] =
ZIO.attemptBlockingIO(scala.io.Source.fromFile(path).mkString)Returns ZIO[Any, IOException, String]
// Effect<A, E, R>
const readFile = (path: string): Effect<
string, // A: file contents
IOException, // E: read error
never // R: no requirements
> =>
Effect.try(() =>
Deno.readTextFileSync(path)
).pipe(
Effect.mapError((e) => new IOException(e.message))
)Returns Effect<string, IOException, never>
Variance
ZIO uses variance annotations (-R, +E, +A). TypeScript doesn't have variance annotations, so Effect types are invariant.
The different parameter order is the biggest source of confusion for ZIO developers moving to Effect. Remember: A, E, R — Success, Error, Requirements.