Skip to content

Instantly share code, notes, and snippets.

@kayac-chang
Created January 30, 2022 12:34
Show Gist options
  • Save kayac-chang/953b37deefa8893cca2868d3390b3851 to your computer and use it in GitHub Desktop.
Save kayac-chang/953b37deefa8893cca2868d3390b3851 to your computer and use it in GitHub Desktop.
[learn FP with Kirby using `fp-ts`] Either
/**
* Chain with Either
* ===
* chain help solve nested either structure
*/
import { flow, pipe } from "fp-ts/lib/function";
import { Password, validate } from "./_prepare";
import { map, Either, chain, right } from "fp-ts/Either";
/**
* Scenerio.
* If hash function may throw error
*/
type HashFn = (value: string) => Either<Error, string>;
type HashExecute = (password: Password) => Either<Error, Password>;
function hash(algorithm: HashFn): HashExecute {
return (password) =>
pipe(
algorithm(password.value),
map((value) => ({
...password,
value,
isHashed: true,
}))
);
}
import { createHash } from "crypto";
/**
* using chain to flatten nested result
*/
const logic = flow(
Password,
validate({ minLength: 8, capitalLetterRequired: true }),
chain(
hash((value) =>
right(
createHash("md5").update(value).digest("hex")
//
)
)
)
);
// test both case
console.log(pipe("pw123", logic));
console.log(pipe("Password123", logic));
/**
* Either (Result)
* ===
* An Either is a type
* that represents a synchronous operation
* that can succeed or failed.
*/
describe(`The Either type is either 'Right' or 'Left'`, () => {
type Either<E, A> = Left<E> | Right<A>;
// Left represents failure
interface Left<E> {
readonly _tag: "Left";
readonly left: E;
}
// Right represents success
interface Right<A> {
readonly _tag: "Right";
readonly right: A;
}
});
/**
* Why use Eithers ?
*
* Eithers are essential for capturing error states,
* we need either because we cannot break pipelines by throwing errors.
*/
/**
* Eithers is always type-safe.
* With using try-catch, the error is always type unknown.
* With Eithers, we know every error state based on type signature.
*/
/**
* Example
* Password Validate
* The password must be at least 8 characters long and have at least 1 capital letter.
* If the password is valid, we will hash it.
*/
// 1. Create two error to for 'min length' and 'at least 1 capital'
function MinLengthValidationError(minLength: number) {
return new Error(
`password fails to meet min length requirement: ${minLength}`
);
}
function CapitalLetterMissingValidationError() {
return new Error(`password is missing a capital letter`);
}
// 2. Define Password type
interface Password {
_tag: "Password";
value: string;
isHashed?: boolean;
isValidated?: boolean;
}
function Password(value: string): Password {
return { _tag: "Password", value };
}
// 3. Validate the Password with Password Specification
import * as Either from "fp-ts/Either";
type PasswordSpecification = {
minLength?: number;
capitalLetterRequired?: boolean;
};
function validate(specification: PasswordSpecification) {
const { minLength = 0, capitalLetterRequired = false } = specification;
return (password: Password) => {
if (password.value.length < minLength) {
return Either.left(MinLengthValidationError(minLength));
}
if (capitalLetterRequired && !/[A-Z]/.test(password.value)) {
return Either.left(CapitalLetterMissingValidationError());
}
return Either.right({ ...password, isValidated: true });
};
}
// 4. Define Hash function
type HashFn = (value: string) => string;
type HashExecute = (password: Password) => Password;
function hash(algorithm: HashFn): HashExecute {
return (password) => ({
...password,
value: algorithm(password.value),
isHashed: true,
});
}
// 5. pipe
import { flow, pipe } from "fp-ts/function";
import { createHash } from "crypto";
const logic = flow(
Password,
validate({ minLength: 8, capitalLetterRequired: true }),
Either.map(
hash((value) =>
//
createHash("md5").update(value).digest("hex")
)
)
);
// 6. Test two scenerio, one for failer, one for succeed
console.log(pipe("pw123", logic));
console.log(pipe("Password123", logic));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment