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.
Initiates a do notation with the current Result, binding it to a context object with the provided key.
bind
Binds a Result to the context object in a do notation.
If the Result is Ok, the value is assigned to the key in the context object. If the Result is Error, the parent Result running the Do simulation becomes an Error.
let
Ensure you know what you're doing when binding a raw value using let, otherwise a thrown exception will not be caught and break your app
Binds a raw value to the context object in a Do-notation.
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.
import { Result } from 'funkcia';
declare function findUserById(id: string): Result;
declare function getUserScore(user: User): Result;
declare function getUserLevel(user: User, score: UserScore): UserLevel;
// ββββ Result
// βΌ
const userLevel = findUserById('user_123')
.bindTo('user')
.bind('score', (ctx) => getUserScore(ctx.user))
.map((ctx) => getUserLevel(ctx.user, ctx.score));
// β²
// ββββ { user: User; score: UserScore }
import { Result } from 'funkcia';
declare function findUserById(id: string): Result;
declare function getUserScore(user: User): Result;
declare function getUserLevel(user: User, score: UserScore): UserLevel;
// ββββ Result
// βΌ
const userLevel = Result.Do
.bind('user', () => findUserById('user_123'))
.bind('score', (ctx) => getUserScore(ctx.user))
.map((ctx) => getUserLevel(ctx.user, ctx.score));
// β²
// ββββ { user: User; score: UserScore }
import { Result } from 'funkcia';
const result = Result.Do
.let('a', () => 10)
.let('b', (ctx) => ctx.a * 2)
.map((ctx) => a + b);
// β²
// ββββ { a: number; b: number }
import { Result } from 'funkcia';
declare function findUser(id: string): Result<User, UserNotFound>;
declare function getUserPermissions(user: User): Result<Permissions, MissingPermissionsError>;
declare function checkAccess(permissions: Permissions, resource: string): Result<Access, InsuficientPermissionsError>;
const access = Result.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'));
import { Result } from 'funkcia';
declare function findUser(id: string): Result<User, UserNotFound>;
declare function getUserPermissions(user: User): Result<Permissions, MissingPermissionsError>;
declare function checkAccess(permissions: Permissions, resource: string): Result<Access, InsuficientPermissionsError>;
const access = findUser('user_123')
.andThen(user =>
getUserPermissions(user)
.andThen(permissions =>
checkAccess(permissions, 'api-key')
)
);
import { Result } from 'funkcia';
declare function findUser(id: string): Result<User, UserNotFound>;
declare function getUserPermissions(user: User): Result<Permissions, MissingPermissionsError>;
declare function checkAccess(permissions: Permissions, resource: string): Result<Access, InsuficientPermissionsError>;
const user = findUser('user_123');
const permissions = user.andThen(getUserPermissions);
const access = permissions.andThen(permissions => {
return checkAccess(permissions, 'admin-panel');
});