🧠
Funkcia Docs
🧠
Funkcia Docs
  • Welcome
  • Data types
    • Option
      • Error Propagation
      • Do Notation
    • Result
      • Error Propagation
      • Do Notation
    • OptionAsync
      • Error Propagation
      • Do Notation
    • ResultAsync
      • Error Propagation
      • Do Notation
  • Modules
    • Exceptions
    • Functions
    • JSON
    • Predicate
    • URI
    • URL
Powered by GitBook
On this page

Was this helpful?

Edit on GitHub
  1. Data types
  2. Option

Do Notation

A do notation syntax allows writing code in a more declarative style, similar to the do notation in other programming languages. It provides a way to define variables and perform operations on them using functions like bind and let, piping the returned values into a context object.

Do

Initiates a do notation for the Option type.

import { Option } from 'funkcia';

declare function findUserById(id: string): Option;
declare function getUserScore(user: User): Option;
declare function getUserLevel(user: User, score: UserScore): UserLevel;

//        ┌─── Option
//        â–¼
const userLevel = Option.Do
  .bind('user', () => findUserById('user_123'))
  .bind('score', () => getUserScore(ctx.user))
  .map((ctx) => getUserLevel(ctx.user, ctx.score));
//       â–²
//       └─── { user: User; score: UserScore }

bindTo

Initiates a do notation with the current Option, binding it to a context object with the provided key.

import { Option } from 'funkcia';

declare function findUserById(id: string): Option;
declare function getUserScore(user: User): Option;
declare function getUserLevel(user: User, score: UserScore): UserLevel;

//        ┌─── Option
//        â–¼
const userLevel = findUserById('user_123')
  .bindTo('user')
  .bind('score', () => getUserScore(ctx.user))
  .map((ctx) => getUserLevel(ctx.user, ctx.score));
//       â–²
//       └─── { user: User; score: UserScore }

bind

Binds an Option to the context object in a do notation.

If the Option is Some, the value is assigned to the key in the context object. If the Option is None, the parent Option running the Do simulation becomes a None.

import { Option } from 'funkcia';

declare function findUserById(id: string): Option;
declare function getUserScore(user: User): Option;
declare function getUserLevel(user: User, score: UserScore): UserLevel;

//        ┌─── Option
//        â–¼
const userLevel = Option.Do
  .bind('user', () => findUserById('user_123'))
  .bind('score', () => getUserScore(ctx.user))
  .map((ctx) => getUserLevel(ctx.user, ctx.score));
//       â–²
//       └─── { user: User; score: UserScore }

let

Binds a raw value to the context object in a Do-notation.

If the value is not nullable, the value is assigned to the key in the context object. If the value is null or undefined, the parent Option running the Do simulation becomes a None.

import { Option } from 'funkcia';

const option = Option.Do
  .let('a', () => 10)
  .let('b', () => ctx.a * 2)
  .map((ctx) => a + b);
//       â–²
//       └─── { a: number; b: number }

Understanding the do notation

Do notation provides a clean way to handle sequences of operations that might fail, where each step depends on the success of all previous steps. Think of it as a chain of dominoes - if any domino falls incorrectly (returns None), the entire sequence stops.

Here's a practical example:

import { Option } from 'funkcia';

declare function findUser(id: string): Option<User>;
declare function getUserPermissions(user: User): Option<Permissions>;
declare function checkAccess(permissions: Permissions, resource: string): Option<Access>;

const access = Option.Do
  // First, try to find the user
  .bind('user', () => findUser('user_123'))
  // If user is found, get their permissions
  .bind('permissions', (ctx) => getUserPermissions(ctx.user))
  // If all steps succeed, we can use the accumulated context to check access to specific resource
  .andThen((ctx) => checkAccess(ctx.permissions, 'api-key'));

The equivalent code would be much more nested:

import { Option } from 'funkcia';

declare function findUser(id: string): Option<User>;
declare function getUserPermissions(user: User): Option<Permissions>;
declare function checkAccess(permissions: Permissions, resource: string): Option<Access>;

const access = findUser('user_123')
  .andThen(user =>
    getUserPermissions(user)
      .andThen(permissions =>
        checkAccess(permissions, 'api-key')
      )
  );

Or with intermediate variables:

import { Option } from 'funkcia';

declare function findUser(id: string): Option<User>;
declare function getUserPermissions(user: User): Option<Permissions>;
declare function checkAccess(permissions: Permissions, resource: string): Option<Access>;

const user = findUser('user_123');
const permissions = user.andThen(getUserPermissions);

const access = permissions.andThen(permissions => {
  return checkAccess(permissions, 'admin-panel');
});
PreviousError PropagationNextResult

Last updated 3 months ago

Was this helpful?