Rename apEitherK
to apSEitherK
+ fix some typos (#127)
* exo8: ap -> apS * Fix typos
This commit is contained in:
parent
93139b9b7b
commit
f867fef92d
8 changed files with 36 additions and 37 deletions
|
@ -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
|
||||
|
|
|
@ -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 //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> =
|
||||
|
|
|
@ -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)({})();
|
||||
|
|
|
@ -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;
|
||||
|
|
Reference in a new issue