Rename `apEitherK` to `apSEitherK` + fix some typos (#127)

* exo8: ap -> apS

* Fix typos
This commit is contained in:
Gabriel Théron 2023-05-12 17:35:11 +02:00 committed by GitHub
parent 93139b9b7b
commit f867fef92d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 36 additions and 37 deletions

View File

@ -2,7 +2,7 @@
// Composing computations with `pipe` and `flow`
// Functional programming is all about composing small functions together like
// lego bricks to build more and more complex computations.
// Lego bricks to build more and more complex computations.
//
// Strictly speaking, composing two functions `f` and `g` means applying the
// result of the first one to the second one. By applying this composition

View File

@ -20,24 +20,22 @@ export const divide = (a: number, b: number): number => {
// Write the safe version (meaning it handles the case where b is 0) of `divide` with signature:
// safeDivide : (a: number, b: number) => Option<number>
//
// HINT: Option has two basic contructors:
// HINT: Option has two basic constructors:
// - `option.some(value)`
// - `option.none`
export const safeDivide: (a: number, b: number) => Option<number> =
unimplemented;
// You probably wrote `safeDivide` using `if` statements and it's perfectly valid!
// There are ways to not use `if` statements.
// Keep in mind that extracting small functions out of pipes and using `if` statements in them
// Keep in mind that extracting small functions out of pipes and using `if` statements in them
// is perfectly fine and is sometimes more readable than not using `if`.
//
// BONUS: Try now to re-write `safeDivide` without any `if`
//
// HINT: Have a look at `fromPredicate` constructor
///////////////////////////////////////////////////////////////////////////////
// EITHER //
///////////////////////////////////////////////////////////////////////////////

View File

@ -10,7 +10,7 @@ import { unimplemented } from '../utils';
// SETUP //
///////////////////////////////////////////////////////////////////////////////
// We are developping a small game, and the player can control either one of
// We are developing a small game, and the player can control either one of
// three types of characters, mainly differentiated by the type of damage they
// can put out.
@ -133,7 +133,7 @@ export const checkTargetAndShoot: (
// OPTION //
///////////////////////////////////////////////////////////////////////////////
// The next three function take a `Character` and optionally return the
// The next three functions take a `Character` and optionally return the
// expected damage type if the unit matches the expected character type.
//
// HINT: These functions represent the public API. But it is heavily

View File

@ -76,6 +76,7 @@ export const naiveGiveCurrencyOfCountryToUser = (
task.map(getCountryCode),
task.map(option.map(getCountryCurrency)),
);
// The result type of this method is: `Task<Option<Task<Currency>>>`
// Not ideal, right? We would need to await the first `Task`, then check if it's
// `Some` to get the `Task` inside and finally await the `Task` to retrieve the
@ -103,7 +104,7 @@ export const getCountryCurrencyOfOptionalCountryCode: (
// `Task<Option<Currency>>`
//
// HINT: You should be able to copy the pipe from naiveGiveCurrencyOfCountryToUser
// and make only few updates of it. `task.chain` helper may be usefull.
// and make only few updates of it. The `task.chain` helper may be useful.
export const giveCurrencyOfCountryToUser: (
countryNameFromUserMock: string,
@ -123,6 +124,7 @@ export const giveCurrencyOfCountryToUser: (
export const getCountryCodeOfCountryNames = (
countryNames: ReadonlyArray<string>,
) => countryNames.map(getCountryCode);
// As expected, we end up with a `ReadonlyArray<Option<CountryCode>>`. We know for
// each item of the array if we have been able to find the corresponding country
// code or not.
@ -134,7 +136,7 @@ export const getCountryCodeOfCountryNames = (
// Doing this allows you to stop the process if you have a `None` to tell the user
// that some countries are not valid or move on with a `ReadonlyArray<CountryCode>>`
// if all are valid.
// Typewise, it means going from `ReadonlyArray<Option<CountryCode>>` to
// Type-wise, it means going from `ReadonlyArray<Option<CountryCode>>` to
// `Option<ReadonlyArray<CountryCode>>`
// This is what traversing array is about.
@ -208,7 +210,7 @@ export const performAsyncComputationInSequence: (
// `Option<Task>` in our example)
// Sometimes, you just have two nested containers that you want to 'invert'. It
// can be because the order of containers is meaningful (like `Either<Option>`
// and `Option<Either>`) because you got them from an external api, as
// and `Option<Either>`) or because you got them from an external api, as
// in the examples.
// In that case, what you need is `sequence`, which you can find in the modules
// that have `traverse`.

View File

@ -12,7 +12,7 @@ import { User } from './domain';
// `Task` -> For async operation
// `Either` -> For computations that may fail
//
// Keep in Mind, A ReaderTaskEither is nothing more than a Reader of a Task of an Either
// Keep in mind, a ReaderTaskEither is nothing more than a Reader of a Task of an Either
// type ReaderTaskEither<Env, Error, Value> = Reader<Env, Task<Either<Error, Value>>>
//
// The ReaderTaskEither module from fp-ts gives us some useful methods to manipulate it.
@ -23,7 +23,6 @@ import { User } from './domain';
// current context. In the following example, we need to fetch a user by its id
// and then we want to return its capitalized.
export const getCapitalizedUserName: (args: {
userId: string;
}) => ReaderTaskEither<
@ -32,7 +31,7 @@ export const getCapitalizedUserName: (args: {
string
> = unimplemented;
// Sometimes you will need to get multiple data before performing an operation
// Sometimes you will need to get multiple data points before performing an operation
// on them. In this case, it is very convenient to use the `Do` notation.
//
// The `Do` notation allows you to enrich the context step-by-step by binding
@ -59,10 +58,10 @@ export const getConcatenationOfTheTwoUserNames: (args: {
// only retrieve after performing some operations, in other words, operations
// need to be sequential.
// For example, if you want to fetch the best friend of a user you will have to
// fetch the first user and then fetch its best friend.
// first fetch the user and then fetch their best friend.
// In this case, we will use `rte.bindW()` to use data of the current context
// (the firstly fetched user) to perform a second operation (fetch its best friend)
// and bind the return value to feed the context and use those data.
// (the firstly fetched user) to perform a second operation (fetch their best friend)
// and bind the return value to feed the context and use this data.
export const getConcatenationOfTheBestFriendNameAndUserName: (args: {
userIdOne: string;

View File

@ -14,7 +14,7 @@ import { unimplemented } from '../utils';
// In fact, it can sometimes be helpful to think of `Set` as a special case
// of `Map` where `Set<T>` is strictly equivalent to `Map<T, void>`.
//
// To manipulate these collections, we need often need to inform `fp-ts` on
// To manipulate these collections, we often need to inform `fp-ts` on
// how to uphold the properties outlined above (eg. how to determine whether
// two elements or keys have the same value, how to combine values together
// in case of key collision or how to order the values when converting back
@ -43,7 +43,7 @@ export const numberArray: ReadonlyArray<number> = [7, 42, 1337, 1, 0, 1337, 42];
export const numberSet: ReadonlySet<number> = unimplemented();
// Convert `numberSet` back to an an array in `numberArrayFromSet`.
// Convert `numberSet` back to an array in `numberArrayFromSet`.
// You need to use the `ReadonlySet` module from `fp-ts` instead of the
// JavaScript standard constructor.
//
@ -118,7 +118,7 @@ export const mapWithLastEntry: ReadonlyMap<number, string> = unimplemented();
// The `string` module may contain what you need ;)
//
// Bonus point:
// Did you find something helpful in the `Semigroup` module that may have been
// Did you find something in the `Semigroup` module that may have been
// helpful in defining `mapWithLastEntry`?
export const mapWithConcatenatedEntries: ReadonlyMap<number, string> =
@ -135,7 +135,7 @@ export const odds = new Set([1, 3, 5, 7, 9]);
// only include the odd numbers that are not prime.
//
// HINT:
// - Be mindful of the order of operands for the operator you will chose.
// - Be mindful of the order of operands for the operator you will choose.
export const nonPrimeOdds: ReadonlySet<number> = unimplemented();
@ -151,7 +151,7 @@ export type Analytics = {
views: number;
};
// These example maps are voluntarily written in a non fp-ts way to not give
// These example Maps are voluntarily written in a non fp-ts way to not give
// away too much obviously ;)
//
// As an exercise for the reader, they may rewrite those with what they've
@ -173,14 +173,14 @@ export const pageViewsB = new Map(
].map(entry => [entry.page, entry]),
);
// Construct the map with the total page views for all the pages in both sources
// Construct the `Map` with the total page views for all the pages in both sources
// of analytics `pageViewsA` and `pageViewsB`.
//
// In case a page appears in both sources, their view count should be summed.
export const allPageViews: ReadonlyMap<string, Analytics> = unimplemented();
// Construct the map with the total page views but only for the pages that
// Construct the `Map` with the total page views but only for the pages that
// appear in both sources of analytics `pageViewsA` and `pageViewsB`.
export const intersectionPageViews: ReadonlyMap<string, Analytics> =

View File

@ -1,8 +1,8 @@
import { either, reader, readerTaskEither as rte } from 'fp-ts';
import { pipe } from 'fp-ts/function';
import {
apEitherK as rteApEitherK,
apEitherKW as rteApEitherKW,
apSEitherK as rteApSEitherK,
apSEitherKW as rteApSEitherKW,
bindEitherK as rteBindEitherK,
bindEitherKW as rteBindEitherKW,
bindReaderK as rteBindReaderK,
@ -52,12 +52,12 @@ describe('exo8', () => {
});
});
describe('apEitherK[W]', () => {
describe('apSEitherK[W]', () => {
it('should be usable in a happy path do-notation rte pipeline', async () => {
const pipeline = await pipe(
rte.Do,
rte.apS('foo', rte.of(42)),
rteApEitherK('bar', either.right(1337)),
rteApSEitherK('bar', either.right(1337)),
)({})();
const expected = await rte.of({ foo: 42, bar: 1337 })({})();
@ -69,8 +69,8 @@ describe('exo8', () => {
const pipeline = await pipe(
rte.Do,
rte.apS('foo', rte.of<unknown, string, number>(42)),
rteApEitherK('bar', either.right(1337)),
rteApEitherK('baz', either.left(`Error!`)),
rteApSEitherK('bar', either.right(1337)),
rteApSEitherK('baz', either.left(`Error!`)),
)({})();
const expected = await rte.left('Error!')({})();
@ -82,8 +82,8 @@ describe('exo8', () => {
const pipeline = await pipe(
rte.Do,
rte.apS('foo', rte.of<unknown, string, number>(42)),
rteApEitherK('bar', either.right(1337)),
rteApEitherKW('baz', either.left(0)),
rteApSEitherK('bar', either.right(1337)),
rteApSEitherKW('baz', either.left(0)),
)({})();
const expected = await rte.left(0)({})();

View File

@ -12,7 +12,7 @@ import { unimplemented } from '../utils';
// `Option`, `Either` and the likes such as `map`, `chain` and so on as
// combinators.
//
// The fp-ts library provides a rich collection for such combinators for each
// The fp-ts library provides a rich collection of such combinators for each
// type and module but sometimes, you may want to reach for a combinator that
// doesn't yet exist in the library and it is useful to know how to define
// your own.
@ -82,22 +82,22 @@ export const bindEitherK: <N extends string, A, E, B>(
export const bindEitherKW = unimplemented;
// Write the implementations and type definitions of `apEitherK` and
// `apEitherKW`.
// Write the implementations and type definitions of `apSEitherK` and
// `apSEitherKW`.
//
// HINT:
// - remember that "widen" in the case of `Either` means the union of the
// possible error types
export const apEitherK = unimplemented;
export const apSEitherK = unimplemented;
export const apEitherKW = unimplemented;
export const apSEitherKW = unimplemented;
// Write the implementations and type definitions of `bindReaderK` and
// `bindReaderKW`.
//
// HINT:
// - remember that "widen" in the case of `Reader` means the interesection of
// - remember that "widen" in the case of `Reader` means the intersection of
// the possible environment types
export const bindReaderK = unimplemented;