2020-07-06 16:49:45 +03:00
|
|
|
# Inato `fp-ts` training
|
|
|
|
|
|
|
|
This repo is a work in progress toward having a comprehensive training material
|
|
|
|
to onboard people on using `fp-ts` efficiently.
|
|
|
|
|
|
|
|
The exercices consist of unimplemented functions and their associated failing
|
|
|
|
tests.
|
|
|
|
|
|
|
|
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
|
|
|
|
```
|
|
|
|
|
2020-12-08 18:51:32 +03:00
|
|
|
You can also run them in watch mode:
|
2022-03-31 17:29:10 +03:00
|
|
|
|
2020-12-08 18:51:32 +03:00
|
|
|
```sh
|
|
|
|
$ yarn test:watch
|
|
|
|
```
|
|
|
|
|
|
|
|
Finally, if you wish to only run the tests for a given exercice `exoN`, you can
|
|
|
|
run the following:
|
2022-03-31 17:29:10 +03:00
|
|
|
|
2020-12-08 18:51:32 +03:00
|
|
|
```sh
|
|
|
|
$ yarn test[:watch] exoN
|
|
|
|
```
|
|
|
|
|
2020-07-06 16:49:45 +03:00
|
|
|
The exercices are organized into `exoN` folders and most of what is required to
|
2022-03-31 17:29:10 +03:00
|
|
|
complete each is detailed in the comments.
|
|
|
|
|
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-06 16:52:18 +03:00
|
|
|
const convertDollarAmountInCountryCodeCurrency =
|
|
|
|
(amountInDollar: number) => (countryCode: CountryCode) =>
|
|
|
|
pipe(
|
|
|
|
getCurrencyFromCountryCode(countryCode),
|
|
|
|
option.map(convertFromDollarAmount(amountInDollar)),
|
|
|
|
option.map(convertedAmount =>
|
|
|
|
console.log(
|
|
|
|
`converted amount for country ${countryCode} is ${convertedAmount}`,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
2022-03-31 17:29:10 +03:00
|
|
|
```
|