This repository has been archived on 2023-05-29. You can view files and clone it, but cannot push or open issues or pull requests.
fp-ts-training/README.md

133 lines
3.6 KiB
Markdown
Raw Permalink Normal View History

2020-07-06 16:49:45 +03:00
# Inato `fp-ts` training
2023-04-21 16:28:36 +03:00
Training material to onboard people on using `fp-ts` efficiently.
2020-07-06 16:49:45 +03:00
2023-04-21 16:28:36 +03:00
The exercises consist of unimplemented functions and their associated failing tests.
2020-07-06 16:49:45 +03:00
2023-04-21 16:31:33 +03:00
If needed, you can always refer to the `solution` branch where you will find the solutions for each exercices.
2023-04-21 16:28:36 +03:00
But first, it is essential to understand why we are using `fp-ts`. We suggest you to read this [article](https://medium.com/inato/our-journey-to-functional-programing-36854a370de1) and then start the exercises.
After cloning the repository, setup the project by running
```sh
$ yarn
```
2020-07-06 16:49:45 +03:00
To run the tests, simply run
2022-03-31 17:29:10 +03:00
2020-07-06 16:49:45 +03:00
```sh
$ yarn test
```
You can also run them in watch mode:
2022-03-31 17:29:10 +03:00
```sh
$ yarn test:watch
```
2023-04-21 16:28:36 +03:00
Finally, if you wish to only run the tests for a given exercise `exoN`, you can run the following:
2022-03-31 17:29:10 +03:00
```sh
$ yarn test[:watch] exoN
```
2023-04-21 16:28:36 +03:00
The exercises are organized into `exoN` folders and most of what is required to complete each is detailed in the comments.
2022-03-31 17:29:10 +03:00
2022-04-01 11:00:29 +03:00
## code style guide
2022-03-31 17:29:10 +03:00
2022-04-01 12:21:53 +03:00
For readability purpose, we replace `ReaderTaskEither` by `rte`
2022-03-31 17:29:10 +03:00
- Use `flow` instead of `pipe` when possible
2022-04-06 15:20:55 +03:00
> Why? Using flow reduces the amount of variables to declare in a method, hence the visibility and readability of the code
2022-03-31 17:29:10 +03:00
```typescript
// Bad
2022-04-01 12:21:53 +03:00
const formatUserPhoneNumber = (user: User) =>
pipe(user, User.phoneNumber, User.formatPhoneNumber);
2022-03-31 17:29:10 +03:00
// Good
2022-04-01 12:21:53 +03:00
const formatUserPhoneNumber = flow(User.phoneNumber, User.formatPhoneNumber);
2022-03-31 17:29:10 +03:00
```
- Avoid using `boolean` method `match` when unecessary
2022-04-01 11:00:29 +03:00
> Why? boolean.match can lower the global understanding of a method and enforce nested pipes. Using classic `if/else` is often the best option
2022-03-31 17:29:10 +03:00
```typescript
// Bad
2022-04-01 12:21:53 +03:00
const triggerEmailCampaign = ({
user,
...emailSettings
}: {
2022-04-06 15:20:55 +03:00
user: User} & EmailSettings) =>
2022-04-01 12:21:53 +03:00
pipe(
user.nationality === 'FR',
boolean.match(
2022-04-06 16:52:18 +03:00
() => triggerGlobalEmailCampaign({ to: user.email, emailSettings }),
2022-04-01 12:21:53 +03:00
() => triggerFrenchEmailCampaign({ to: user.email, emailSettings }),
),
);
2022-03-31 17:29:10 +03:00
// Good
2022-04-01 12:21:53 +03:00
const triggerEmailCampaign = ({
user,
2022-04-06 15:20:55 +03:00
...emailSettings
}: { user: User } & EmailSettings) => {
2022-04-01 12:21:53 +03:00
if (user.nationality === 'FR') {
return triggerFrenchEmailCampaign({ to: user.email, emailSettings });
2022-03-31 17:29:10 +03:00
}
2022-04-06 16:52:18 +03:00
return triggerGlobalEmailCampaign({ to: user.email, emailSettings });
2022-03-31 17:29:10 +03:00
```
2022-04-01 12:21:53 +03:00
- Avoid nested pipes
> Why? They lower global understanding of the code. We allow ourselves 2 levels of piping maximum per function and tend to do atomic functions instead
2022-03-31 17:29:10 +03:00
```typescript
// Bad
2022-04-06 16:52:18 +03:00
export const convertDollarAmountInCountryCurrency = ({
2022-04-06 16:26:42 +03:00
countryName,
amountInDollar,
}: {
countryName: CountryName;
amountInDollar: number;
}) =>
2022-04-01 12:21:53 +03:00
pipe(
2022-04-06 16:26:42 +03:00
getCountryCode(countryName),
either.map(
countryCode =>
2022-04-06 15:20:55 +03:00
pipe(
2022-04-06 16:26:42 +03:00
getCountryCurrency(countryCode),
2022-04-06 16:52:18 +03:00
option.map(
flow(
convertFromDollarAmount(amountInDollar),
2022-04-06 16:26:42 +03:00
convertedAmount =>
console.log(
`converted amount for country ${countryCode} is ${convertedAmount}`,
),
),
),
2022-04-01 12:21:53 +03:00
),
),
),
);
2022-03-31 17:29:10 +03:00
// Good
2022-04-06 16:52:18 +03:00
export const convertDollarAmountInCountryCurrency = (amountInDollar: number) =>
flow(
getCountryCode,
either.map(convertDollarAmountToCountryCodeCurrency(amountInDollar)),
2022-04-01 12:21:53 +03:00
);
2022-04-06 16:26:42 +03:00
2022-04-20 14:32:52 +03:00
const convertDollarAmountToCountryCodeCurrency =
2022-04-06 16:52:18 +03:00
(amountInDollar: number) => (countryCode: CountryCode) =>
pipe(
2022-04-20 14:32:52 +03:00
getCountryCurrency(countryCode),
2022-04-06 16:52:18 +03:00
option.map(convertFromDollarAmount(amountInDollar)),
option.map(convertedAmount =>
console.log(
`converted amount for country ${countryCode} is ${convertedAmount}`,
),
),
);
2022-03-31 17:29:10 +03:00
```