Do-Notation

Scala-like for-comprehensions

Compose monadic operations elegantly without nested flatMap calls

Do-Notation

Scala-like for-comprehensions for composing monadic operations.

Overview

Do-notation provides generator-based monadic comprehensions inspired by Scala’s for-comprehensions. It makes complex monadic chains readable and maintainable by eliminating nested flatMap calls.

Basic Usage

import { Do, $ } from "functype/do";
import { Option } from "functype/option";

// Instead of nested flatMaps
const nested = Option(1).flatMap((a) =>
  Option(2).flatMap((b) => Option(3).map((c) => a + b + c)),
);

// Use Do-notation
const clean = Do(function* () {
  const a = yield* $(Option(1));
  const b = yield* $(Option(2));
  const c = yield* $(Option(3));
  return a + b + c;
}); // Some(6)

The $ Helper

The $ function enables TypeScript type inference with generators:

import { Do, $ } from "functype/do";

const result = Do(function* () {
  const x = yield* $(Option(42)); // x is number, not unknown
  return x * 2;
});

Short-Circuiting

Do-notation automatically propagates None/Left/Failure:

// Option - stops on None
const result = Do(function* () {
  const a = yield* $(Option(1));
  const b = yield* $(Option.none<number>()); // stops here
  const c = yield* $(Option(3));
  return a + b + c;
}); // None

// Either - stops on Left
const validated = Do(function* () {
  const name = yield* $(validateName(input)); // Left stops chain
  const email = yield* $(validateEmail(input));
  return { name, email };
});

Supported Types

Do-notation works with any monad in functype:

Option

const result = Do(function* () {
  const user = yield* $(findUser(id));
  const profile = yield* $(user.profile);
  const email = yield* $(profile.email);
  return email;
}); // Option<string>

Either

const result = Do(function* () {
  const name = yield* $(validateName(input.name));
  const age = yield* $(validateAge(input.age));
  const email = yield* $(validateEmail(input.email));
  return { name, age, email };
}); // Either<ValidationError, User>

Try

const result = Do(function* () {
  const config = yield* $(Try(() => readConfig()));
  const db = yield* $(Try(() => connectDB(config)));
  const users = yield* $(Try(() => db.query("SELECT * FROM users")));
  return users;
}); // Try<User[]>

List (Cartesian Products)

const combinations = Do(function* () {
  const x = yield* $(List([1, 2, 3]));
  const y = yield* $(List(["a", "b"]));
  return `${x}${y}`;
}); // List(["1a", "1b", "2a", "2b", "3a", "3b"])

Async Do-Notation

For async operations, use DoAsync:

import { DoAsync, $ } from "functype/do";

const result = await DoAsync(async function* () {
  const user = yield* $(Task.async("getUser", () => fetchUser()));
  const posts = yield* $(Task.async("getPosts", () => fetchPosts(user.id)));
  return { user, posts };
});

Performance

Do-notation is highly optimized:

  • Option/Either/Try: Near-zero overhead
  • List comprehensions: 175x faster than nested flatMaps
  • Uses direct iteration instead of creating intermediate structures

Key Features

  • Scala-Inspired: Similar syntax to Scala’s for-comprehensions
  • Type-Safe: Full TypeScript inference with the $ helper
  • Short-Circuiting: None/Left/Failure automatically propagates
  • High Performance: Optimized for List comprehensions

When to Use Do-Notation

  • Chaining 3+ monadic operations
  • List comprehensions (cartesian products)
  • Complex validation pipelines
  • When readability matters more than micro-optimization

Comparison

// Without Do-notation
const result = getUser(id).flatMap((user) =>
  getProfile(user.profileId).flatMap((profile) =>
    getSettings(profile.settingsId).map((settings) => ({
      user,
      profile,
      settings,
    })),
  ),
);

// With Do-notation
const result = Do(function* () {
  const user = yield* $(getUser(id));
  const profile = yield* $(getProfile(user.profileId));
  const settings = yield* $(getSettings(profile.settingsId));
  return { user, profile, settings };
});

API Reference

See full API documentation at functype API docs