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`
|
// Composing computations with `pipe` and `flow`
|
||||||
|
|
||||||
// Functional programming is all about composing small functions together like
|
// 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
|
// Strictly speaking, composing two functions `f` and `g` means applying the
|
||||||
// result of the first one to the second one. By applying this composition
|
// result of the first one to the second one. By applying this composition
|
||||||
|
|
|
@ -20,14 +20,13 @@ 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:
|
// Write the safe version (meaning it handles the case where b is 0) of `divide` with signature:
|
||||||
// safeDivide : (a: number, b: number) => Option<number>
|
// safeDivide : (a: number, b: number) => Option<number>
|
||||||
//
|
//
|
||||||
// HINT: Option has two basic contructors:
|
// HINT: Option has two basic constructors:
|
||||||
// - `option.some(value)`
|
// - `option.some(value)`
|
||||||
// - `option.none`
|
// - `option.none`
|
||||||
|
|
||||||
export const safeDivide: (a: number, b: number) => Option<number> =
|
export const safeDivide: (a: number, b: number) => Option<number> =
|
||||||
unimplemented;
|
unimplemented;
|
||||||
|
|
||||||
|
|
||||||
// You probably wrote `safeDivide` using `if` statements and it's perfectly valid!
|
// You probably wrote `safeDivide` using `if` statements and it's perfectly valid!
|
||||||
// There are ways to not use `if` statements.
|
// 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
|
||||||
|
@ -37,7 +36,6 @@ export const safeDivide: (a: number, b: number) => Option<number> =
|
||||||
//
|
//
|
||||||
// HINT: Have a look at `fromPredicate` constructor
|
// HINT: Have a look at `fromPredicate` constructor
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// EITHER //
|
// EITHER //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { unimplemented } from '../utils';
|
||||||
// SETUP //
|
// 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
|
// three types of characters, mainly differentiated by the type of damage they
|
||||||
// can put out.
|
// can put out.
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ export const checkTargetAndShoot: (
|
||||||
// OPTION //
|
// 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.
|
// expected damage type if the unit matches the expected character type.
|
||||||
//
|
//
|
||||||
// HINT: These functions represent the public API. But it is heavily
|
// HINT: These functions represent the public API. But it is heavily
|
||||||
|
|
|
@ -76,6 +76,7 @@ export const naiveGiveCurrencyOfCountryToUser = (
|
||||||
task.map(getCountryCode),
|
task.map(getCountryCode),
|
||||||
task.map(option.map(getCountryCurrency)),
|
task.map(option.map(getCountryCurrency)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// The result type of this method is: `Task<Option<Task<Currency>>>`
|
// 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
|
// 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
|
// `Some` to get the `Task` inside and finally await the `Task` to retrieve the
|
||||||
|
@ -103,7 +104,7 @@ export const getCountryCurrencyOfOptionalCountryCode: (
|
||||||
// `Task<Option<Currency>>`
|
// `Task<Option<Currency>>`
|
||||||
//
|
//
|
||||||
// HINT: You should be able to copy the pipe from naiveGiveCurrencyOfCountryToUser
|
// 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: (
|
export const giveCurrencyOfCountryToUser: (
|
||||||
countryNameFromUserMock: string,
|
countryNameFromUserMock: string,
|
||||||
|
@ -123,6 +124,7 @@ export const giveCurrencyOfCountryToUser: (
|
||||||
export const getCountryCodeOfCountryNames = (
|
export const getCountryCodeOfCountryNames = (
|
||||||
countryNames: ReadonlyArray<string>,
|
countryNames: ReadonlyArray<string>,
|
||||||
) => countryNames.map(getCountryCode);
|
) => countryNames.map(getCountryCode);
|
||||||
|
|
||||||
// As expected, we end up with a `ReadonlyArray<Option<CountryCode>>`. We know for
|
// 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
|
// each item of the array if we have been able to find the corresponding country
|
||||||
// code or not.
|
// 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
|
// 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>>`
|
// that some countries are not valid or move on with a `ReadonlyArray<CountryCode>>`
|
||||||
// if all are valid.
|
// 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>>`
|
// `Option<ReadonlyArray<CountryCode>>`
|
||||||
// This is what traversing array is about.
|
// This is what traversing array is about.
|
||||||
|
|
||||||
|
@ -208,7 +210,7 @@ export const performAsyncComputationInSequence: (
|
||||||
// `Option<Task>` in our example)
|
// `Option<Task>` in our example)
|
||||||
// Sometimes, you just have two nested containers that you want to 'invert'. It
|
// 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>`
|
// 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 the examples.
|
||||||
// In that case, what you need is `sequence`, which you can find in the modules
|
// In that case, what you need is `sequence`, which you can find in the modules
|
||||||
// that have `traverse`.
|
// that have `traverse`.
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { User } from './domain';
|
||||||
// `Task` -> For async operation
|
// `Task` -> For async operation
|
||||||
// `Either` -> For computations that may fail
|
// `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>>>
|
// 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.
|
// 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
|
// current context. In the following example, we need to fetch a user by its id
|
||||||
// and then we want to return its capitalized.
|
// and then we want to return its capitalized.
|
||||||
|
|
||||||
|
|
||||||
export const getCapitalizedUserName: (args: {
|
export const getCapitalizedUserName: (args: {
|
||||||
userId: string;
|
userId: string;
|
||||||
}) => ReaderTaskEither<
|
}) => ReaderTaskEither<
|
||||||
|
@ -32,7 +31,7 @@ export const getCapitalizedUserName: (args: {
|
||||||
string
|
string
|
||||||
> = unimplemented;
|
> = 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.
|
// 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
|
// 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
|
// only retrieve after performing some operations, in other words, operations
|
||||||
// need to be sequential.
|
// need to be sequential.
|
||||||
// For example, if you want to fetch the best friend of a user you will have to
|
// 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
|
// 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)
|
// (the firstly fetched user) to perform a second operation (fetch their best friend)
|
||||||
// and bind the return value to feed the context and use those data.
|
// and bind the return value to feed the context and use this data.
|
||||||
|
|
||||||
export const getConcatenationOfTheBestFriendNameAndUserName: (args: {
|
export const getConcatenationOfTheBestFriendNameAndUserName: (args: {
|
||||||
userIdOne: string;
|
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
|
// 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>`.
|
// 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
|
// 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
|
// 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
|
// 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();
|
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
|
// You need to use the `ReadonlySet` module from `fp-ts` instead of the
|
||||||
// JavaScript standard constructor.
|
// JavaScript standard constructor.
|
||||||
//
|
//
|
||||||
|
@ -118,7 +118,7 @@ export const mapWithLastEntry: ReadonlyMap<number, string> = unimplemented();
|
||||||
// The `string` module may contain what you need ;)
|
// The `string` module may contain what you need ;)
|
||||||
//
|
//
|
||||||
// Bonus point:
|
// 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`?
|
// helpful in defining `mapWithLastEntry`?
|
||||||
|
|
||||||
export const mapWithConcatenatedEntries: ReadonlyMap<number, string> =
|
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.
|
// only include the odd numbers that are not prime.
|
||||||
//
|
//
|
||||||
// HINT:
|
// 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();
|
export const nonPrimeOdds: ReadonlySet<number> = unimplemented();
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ export type Analytics = {
|
||||||
views: number;
|
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 ;)
|
// away too much obviously ;)
|
||||||
//
|
//
|
||||||
// As an exercise for the reader, they may rewrite those with what they've
|
// 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]),
|
].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`.
|
// of analytics `pageViewsA` and `pageViewsB`.
|
||||||
//
|
//
|
||||||
// In case a page appears in both sources, their view count should be summed.
|
// In case a page appears in both sources, their view count should be summed.
|
||||||
|
|
||||||
export const allPageViews: ReadonlyMap<string, Analytics> = unimplemented();
|
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`.
|
// appear in both sources of analytics `pageViewsA` and `pageViewsB`.
|
||||||
|
|
||||||
export const intersectionPageViews: ReadonlyMap<string, Analytics> =
|
export const intersectionPageViews: ReadonlyMap<string, Analytics> =
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { either, reader, readerTaskEither as rte } from 'fp-ts';
|
import { either, reader, readerTaskEither as rte } from 'fp-ts';
|
||||||
import { pipe } from 'fp-ts/function';
|
import { pipe } from 'fp-ts/function';
|
||||||
import {
|
import {
|
||||||
apEitherK as rteApEitherK,
|
apSEitherK as rteApSEitherK,
|
||||||
apEitherKW as rteApEitherKW,
|
apSEitherKW as rteApSEitherKW,
|
||||||
bindEitherK as rteBindEitherK,
|
bindEitherK as rteBindEitherK,
|
||||||
bindEitherKW as rteBindEitherKW,
|
bindEitherKW as rteBindEitherKW,
|
||||||
bindReaderK as rteBindReaderK,
|
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 () => {
|
it('should be usable in a happy path do-notation rte pipeline', async () => {
|
||||||
const pipeline = await pipe(
|
const pipeline = await pipe(
|
||||||
rte.Do,
|
rte.Do,
|
||||||
rte.apS('foo', rte.of(42)),
|
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 })({})();
|
const expected = await rte.of({ foo: 42, bar: 1337 })({})();
|
||||||
|
@ -69,8 +69,8 @@ describe('exo8', () => {
|
||||||
const pipeline = await pipe(
|
const pipeline = await pipe(
|
||||||
rte.Do,
|
rte.Do,
|
||||||
rte.apS('foo', rte.of<unknown, string, number>(42)),
|
rte.apS('foo', rte.of<unknown, string, number>(42)),
|
||||||
rteApEitherK('bar', either.right(1337)),
|
rteApSEitherK('bar', either.right(1337)),
|
||||||
rteApEitherK('baz', either.left(`Error!`)),
|
rteApSEitherK('baz', either.left(`Error!`)),
|
||||||
)({})();
|
)({})();
|
||||||
|
|
||||||
const expected = await rte.left('Error!')({})();
|
const expected = await rte.left('Error!')({})();
|
||||||
|
@ -82,8 +82,8 @@ describe('exo8', () => {
|
||||||
const pipeline = await pipe(
|
const pipeline = await pipe(
|
||||||
rte.Do,
|
rte.Do,
|
||||||
rte.apS('foo', rte.of<unknown, string, number>(42)),
|
rte.apS('foo', rte.of<unknown, string, number>(42)),
|
||||||
rteApEitherK('bar', either.right(1337)),
|
rteApSEitherK('bar', either.right(1337)),
|
||||||
rteApEitherKW('baz', either.left(0)),
|
rteApSEitherKW('baz', either.left(0)),
|
||||||
)({})();
|
)({})();
|
||||||
|
|
||||||
const expected = await rte.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
|
// `Option`, `Either` and the likes such as `map`, `chain` and so on as
|
||||||
// combinators.
|
// 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
|
// 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
|
// doesn't yet exist in the library and it is useful to know how to define
|
||||||
// your own.
|
// your own.
|
||||||
|
@ -82,22 +82,22 @@ export const bindEitherK: <N extends string, A, E, B>(
|
||||||
|
|
||||||
export const bindEitherKW = unimplemented;
|
export const bindEitherKW = unimplemented;
|
||||||
|
|
||||||
// Write the implementations and type definitions of `apEitherK` and
|
// Write the implementations and type definitions of `apSEitherK` and
|
||||||
// `apEitherKW`.
|
// `apSEitherKW`.
|
||||||
//
|
//
|
||||||
// HINT:
|
// HINT:
|
||||||
// - remember that "widen" in the case of `Either` means the union of the
|
// - remember that "widen" in the case of `Either` means the union of the
|
||||||
// possible error types
|
// 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
|
// Write the implementations and type definitions of `bindReaderK` and
|
||||||
// `bindReaderKW`.
|
// `bindReaderKW`.
|
||||||
//
|
//
|
||||||
// HINT:
|
// 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
|
// the possible environment types
|
||||||
|
|
||||||
export const bindReaderK = unimplemented;
|
export const bindReaderK = unimplemented;
|
||||||
|
|
Reference in a new issue