# Introduction

This post is an express tour for impatients who want to use fp-ts. In this brief introduction, we don’t go through the what is functional programming as well as its advantages/disadvantages.

You don’t really need to understand all mathematics concepts in order to learn functional programming. IMO, you only need to to know how each operator works. Once you get to know each basic operators in fp-ts, you can go back and review the mathematic theory.

# Practical guide to fp-ts

## Pipe, flow

### Pipe

In fp-ts pipe is a function, but in pure functional programming language (like Haskell) it’s an operator. Javascript also has a proposal for it (see Pipe Operator (|>) for JavaScript)

It’s the basic building block of fp-ts, you can use pipe() to chain the sequence of functions from left to right

Let’s look at this simple example:

import { pipe } from "fp-ts/lib/function";

(first: number) =>
(second: number): number => {
return first + second;
};

// This is equivalent to this



The result of this operation is 5. It’s self-explanatory but we can look at these steps:

1. We start with the value of 1.
2. 1 is piped into the first argument of add1 and add1 is evaluated to 2 by adding 1.
3. The return value of add12 is piped into the first argument add3  and is evaluated to 5 by adding 3.

At this point, the pipe receives a number as input and output a new number, but we can also do something else like input a string from a number also.

const meowify = (num: number): string => {
return "meow ".repeat(num).trim();
};



Notes that we cannot put the meowify function in between add1 and add3 function like this

❌ pipe(1, add1, meowify, add3)


### Flow

The flow operator is very similar to pipe operator, the difference is the first argument of flow must be a function. For example, we can use the three functions above to form a flow like this:

import { flow, pipe } from "fp-ts/lib/function";

// Or we can use it like this



In the example with pipe what if we don’t want to feed 1 as the input to the pipe? We probably have to do this:

const meowify1 = (n: number) => pipe(n, flow(add1, add3, meowify)

// but with flow you don't need to



Tip: If you have a long curried functions, you can use ap from Identity monad to apply all arguments

import { ap } from "fp-ts/lib/Identity";

const makeUrl = (protocol: string) => (domain: string) => (port: number) => {
return ${protocol}://${domain}:${port}; }; // ✅ right pipe(makeUrl, ap("https"), ap("swappie.com"), ap(80)); // https://swappie.com:80 // Equivalent to makeUrl("https")("swappie.com")(80); // ❌ this doesn't work pipe("https://", "swappie.com", 80, makeUrl);  ## Option, Either ### Option Options are containers, or specifically an Option is a monad (it’s analogous to Maybe monad in Haskell), that wrap values that could be truthy or falsy. If the values are truthy, we say the Option is of Some type, and if the values are falsy (undefined | null) we say it has the None type. type Option<A> = None | Some<A>;  “Why should we use Option types in the first place?” You might ask. We already know that Typescript already has good ways to deal with undefined or null values. For example, we can use optional chaining or nullish coalescing. The anwser is: mostly you won’t need to use Option, optional chaining can do it as well for you. However the Option type is more than just checking for null. Options can be used to represent failing operations, and most importantly you can chain them or in other words you can compose functions that return Option into a more complex one. In most cases you probably don’t need Option, but let see these example to see some benefits of Option monad const findUrl = (array: string[]): string | undefined => array.find((item) => item.startsWith("http")); const makeA = (url: string | undefined): string => url ? <a href=${url}>${url}</a> : "no link"; const parseLink = (array: string[]): string => makeA(findUrl(array)); // execute const input = ["[", "google", "]", "(", "http://www.google.com", ")"]; console.log(parseLink(input)); // <a href=http://www.google.com>http://www.google.com</a> console.log(parseLink([])); // no link  The code above can be converted to FP style import * as O from "fp-ts/lib/Option"; // O.fromNullable convert a non-nullable value to Some(value) and nullable // to None const findUrl = (array: string[]): O.Option<string> => O.fromNullable(array.find((item) => item.startsWith("http"))); const makeA = (url: string): string => <a href=${url}>${url}</a>; const parseLink = flow( findUrl, O.fold(() => "no link", makeA) ); parseLink(input); // <a href=http://www.google.com>http://www.google.com</a> parseLink([]); // no link  💡 Notes that you can lift a nullable value to an Option using O.fromNullable ### Either An Either is a type that represents a synchronous operation that can succeed or fail. Much like Option, where it is Some or None, the Either type is either Right or LeftRight represents success and Left represents failure. It is analogous to the Result type in Rust. This is one practical example of using fp-ts, specially Either for validating a password strength. For each individual functions below, I think they pretty self-explanatory excepts the last one validatePassword import * as E from "fp-ts/Either"; import * as F from "fp-ts/function"; const minxLength = (s: string): E.Either<Error, string> => { return s.length < 8 ? E.left(new Error("Password is too short")) : E.right(s); }; const oneCapital = (s: string): E.Either<Error, string> => /[A-Z]/g.test(s) ? E.right(s) : E.left(new Error("at least one capital letter")); const oneNumber = (s: string): E.Either<Error, string> => /[0-9]/g.test(s) ? E.right(s) : E.left(new Error("at least one number")); // This also works // F.pipe(minLength(s), E.chain(oneCapital), E.chain(oneNumber)); const validatePassword = (s: string): E.Either<Error, string> => F.pipe(s, minLength, E.chain(oneCapital), E.chain(oneNumber)); // validatePassword('123456'); // Error: at least one capital letter // validatePassword('salaSANA123'); // salaSANA123  We can looks at the break-down steps of validatePassword as follow: Let’s take this happy path example validatePassword('salaSANA123') 1. We will start with input salaSANA123 is passed to minLength 1.1 It will be evaluated to a Either value that contains a right value salaSANA123 2. The return value of minLength('salaSANA123') value will be piped to E.chain(oneCapital) 2.1 E.chain will unwrap the E.right('salaSANA123') to 'salaSANA123' value and passes it to oneCapital. 2.2 oneCapital('salaSANA123') will evaluate the string and returns E.right('salaSANA123'). 3. Again, the return value of oneCapital('salaSANA123') will be piped to E.chain(oneNumber) 3.1 E.chain will unwrap the E.right('salaSANA123') to 'salaSANA123' value and passes it to oneNumber 3.2 oneNumber('salaSANA123') will evaluate the string and returns E.right('salaSANA123') In any situation that one of the three function returns E.left(new Error('...')) the left value is returned immediately 💡 And just like how you can lift nullable into an Option, you can also lift an Option into another fp-ts container, like Either. const minLength = (s: string): O.Option<string> => s.length >= 6 ? O.some(s) : O.none; ... const validatePassword = (s: string): Either<Error, string> => pipe( minLength(s), E.fromOption(() => new Error("at least 6 characters")), // chain(oneCapital), chain(oneNumber) );  ## Task, TaskEither ### Task In fp-ts, a Task is basically a js Promise, this is the definition of Task. interface Task<A> { (): Promise<A>; }  From the docs Task<A> represents an asynchronous computation that yields a value of type A and never fails. If you want to represent an asynchronous computation that may fail, please see TaskEither. ### TaskEither Basically TaskEither = Task + Either, so with TaskEither you can have a Task that may fail. import axios, { AxiosResponse } from "axios"; import * as F from "fp-ts/function"; import * as E from "fp-ts/Either"; import * as T from "fp-ts/Task"; import * as TE from "fp-ts/TaskEither"; type ToDo = { userId: number; id: number; title: string; completed: boolean; }; const safeGet = (url: string): TE.TaskEither<Error, AxiosResponse> => TE.tryCatch(() => axios.get(url), E.toError); const fetchTodo = (id: number): TE.TaskEither<string, ToDo> => F.pipe( safeGet(https://jsonplaceholder.typicode.com/todos/${id}),
TE.fold(
(e: Error) => T.of(e.message),
(a: AxiosResponse) => T.of(a.data)
)
);

const main = async () => {
const resp = await fetchTodo(1)();
// { userId: 1, id: 1, title: 'delectus aut autem', completed: false }
console.log(resp);

const resp1 = await fetchTodo(3)();
// { userId: 1, id: 3, title: 'fugiat veniam minus', completed: false }
console.log(resp1);

const resp2 = await fetchTodo(0)();
// Request failed with status code 404
console.log(resp2);
};

main();


The TE.fold function is actually very simple, it accepts two functions (onLeft, onRight) and it will call onLeft on left value and onRight on right value, depends on the previous value from the pipe.

## Do Notation

From the docs

Both Haskell and PureScript languages provide syntactic sugar for working with monads in the form of do notation.

fp-ts provides it’s own implementation of do notation which can help to simplify effectful code.

You can read about the “official” explanation and example from fp-ts (see this) about do notation in fp-ts

Generally speaking, “do notation” allows you bind previous returned values from other functions in the pipe to a context object. Without do notation it’s very hard to maintain the variable scope, since you either to pass it along as intermediate result or go deep into nested pipe.

import * as TE from "fp-ts/TaskEither";
import { pipe } from "fp-ts/function";
return TE.right(UserId-${username}); }; const createOrder = (userId: string): TE.TaskEither<Error, string> => { return TE.right(Order-${userId});
};

const createOrderRow = (
orderId: string,
userId: string
return TE.right(OrderRowFor-${orderId}-${userId});
};

// This will return something like
// {
//   _tag: 'Right',
//   right: {
//     userId: 'UserIdRick',
//     orderId: 'Order123456-UserIdRick',
//     orderRowId: 'OrderRowFor-UserIdRick-Order123456-UserIdRick'
//   }
// }
const main = pipe(
TE.Do,
TE.bind("userId", () => createUser("Rick")),
TE.bind("orderId", ({ userId }) => createOrder(userId)),
TE.bind("orderRowId", ({ userId, orderId }) =>
createOrderRow(userId, orderId)
),
TE.map(({ userId, orderId, orderRowId }) => ({
userId,
orderId,
orderRowId,
}))
);