Introduction

This is a collection of examples introducing Effect in an example/use case based approach. Everything in here is explained using examples that you can directly copy and play around with. Also you will very quickly see the advantages of using Effect. The content starts with the concepts a JavaScript developer is most familiar with and later adds new conecpts and alternative patterns. It is suitable for beginners who want to get started quickly with effect and add one concept at a time to their knowledge. We assume you are familiar with the Effect type

First Program

This simple program already shows the core concept of effect. After importing Effect, we can construct agenerator functionwith it. Don't worry about understanding what generators do for now, just think of it as an Effect function. So, `program` is an Effect. Now, we just need to execute the program with Effect.run*

const program = Effect.gen(function* (_) {
    console.log('Hello World!');
});

Effect.runSync(program);

Effect Type in Action

Typically, a function can return a value or throw an Error. In Effect, we achieve this by returning an Effect of type Effect<value, Error>. Effect.succeed() "wraps" a value in an Effect and becomes type Effect<value, never>. Effect.fail() "wraps" an Error in an Effect and becomes type Effect<never, Error>. All return types are combined into a single Effect return type - Effect<value, Error>.

const divide = (a: number, b: number): Effect.Effect<number, Error> =>
    b === 0 ? Effect.fail(new Error('Cannot divide by zero')) : Effect.succeed(a / b);

const program = Effect.gen(function* (_) {
    const quotient = yield* _(divide(1, 2));
    console.log(quotient);
});

Effect.runSync(program);
Here, we type the return type of divide() manually, because TypeScript has trouble combining Effect.succeed() and Effect.fail(). To resolve this, we can wrap the return type in Effect.suspend()

// Effect<number, Error>
const divide = (a: number, b: number) =>
    Effect.suspend(() => (b === 0 ? Effect.fail(new Error('Cannot divide by zero')) : Effect.succeed(a / b)));

const program = Effect.gen(function* (_) {
    const quotient = yield* _(divide(1, 2));
    console.log(quotient);
});

Effect.runSync(program);
This is a lot to think about, so here is another example using a generator function achieving the same. Notice that we construct the error with Effect.fail(). This is the only way to manually tell Effect that this is an error and therefore belongs in the error channel (the second parameter of the Effect type). But error is now of type Effect and divide2() is an Effect.gen(), which itself is already an Effect. So if we would return `error` itself, it would result in an Effect inside an Effect with the type `Effect.Effect<number | Effect.Effect<never, Error, never&gt, never, never&gt` **and we never want that**! Therefore we have to execute/yield the error before returning. this "unwraps" the Effect. So, to return an effect type we can either return effects (e.g. with effect.succeed() and Effect.fail()) in normal functions, or use Effect.gen() and return the value as usual and the error with yield\* \_(Effect.fail()). Notice how if we return `error` directly, the error is even in the success channel (the first paramenter of the Effect type), so before executing the error Effect, it does not even notice that it only contains an error.

const divide = (a: number, b: number) =>
    Effect.suspend(() => (b === 0 ? Effect.fail(new Error('Cannot divide by zero')) : Effect.succeed(a / b)));

// this is achieves the same
const divide2 = (a: number, b: number) =>
    Effect.gen(function* (_) {
        if (b === 0) {
            // Effect.Effect<never, Error, never>
            const error = Effect.fail(new Error('Cannot divide by zero'));
            const yieldedError = yield* _(error);
            return yieldedError;
        } else {
            return a / b;
        }
    });

const program = Effect.gen(function* (_) {
    const result1 = yield* _(divide(1, 2));
    const result2 = yield* _(divide2(1, 2));
    console.log(result1, result2);
});

Effect.runSync(program);
This is really the core concept of Effect, so the hardest part should be done. 🎉 🎉