🎨 Switch to the preferred way of importing fp-ts

This commit is contained in:
Hugo Saracino 2022-01-11 14:48:20 +01:00
parent 9102b3da54
commit 53440267bd
7 changed files with 116 additions and 148 deletions

View file

@ -1,8 +1,5 @@
import * as decod from 'decod'; import * as decod from 'decod';
import * as Either from 'fp-ts/lib/Either'; import { either, task } from 'fp-ts';
import * as TaskEither from 'fp-ts/lib/TaskEither';
import * as Task from 'fp-ts/lib/Task';
import { pipe } from 'fp-ts/lib/pipeable';
const decodeErrorMessage = decod.at('message', decod.string); const decodeErrorMessage = decod.at('message', decod.string);
@ -70,16 +67,9 @@ export class Failure<T extends string = FailureType> {
throw failure.toError(); throw failure.toError();
}; };
static eitherUnsafeGet = <A>(either: Either.Either<Failure, A>): A => static eitherUnsafeGet = either.getOrElseW(Failure.throw);
pipe(either, Either.getOrElseW(Failure.throw));
static taskEitherUnsafeGet = <A>( static taskEitherUnsafeGet = task.map(this.eitherUnsafeGet);
taskEither: TaskEither.TaskEither<Failure, A>,
): Task.Task<A> =>
pipe(
taskEither,
TaskEither.getOrElseW((failure) => async () => Failure.throw(failure)),
);
} }
export type FailureTypes<F extends Record<string, string>> = F[keyof F]; export type FailureTypes<F extends Record<string, string>> = F[keyof F];

View file

@ -1,6 +1,4 @@
import * as Either from 'fp-ts/lib/Either'; import { either, option } from 'fp-ts';
import * as Option from 'fp-ts/lib/Option';
import { import {
divide, divide,
DivisionByZero, DivisionByZero,
@ -24,26 +22,26 @@ describe('exo1', () => {
describe('safeDivide', () => { describe('safeDivide', () => {
it('should return the result of dividing two numbers', () => { it('should return the result of dividing two numbers', () => {
expect(safeDivide(25, 5)).toStrictEqual(Option.some(5)); expect(safeDivide(25, 5)).toStrictEqual(option.some(5));
}); });
it('should return Option.none if the denominator is zero', () => { it('should return option.none if the denominator is zero', () => {
expect(safeDivide(25, 0)).toStrictEqual(Option.none); expect(safeDivide(25, 0)).toStrictEqual(option.none);
expect(safeDivide(-25, 0)).toStrictEqual(Option.none); expect(safeDivide(-25, 0)).toStrictEqual(option.none);
}); });
}); });
describe('safeDivideWithError', () => { describe('safeDivideWithError', () => {
it('should return the result of dividing two numbers', () => { it('should return the result of dividing two numbers', () => {
expect(safeDivideWithError(25, 5)).toStrictEqual(Either.right(5)); expect(safeDivideWithError(25, 5)).toStrictEqual(either.right(5));
}); });
it('should return Either.left(DivisionByZero) if the denominator is zero', () => { it('should return either.left(DivisionByZero) if the denominator is zero', () => {
expect(safeDivideWithError(25, 0)).toStrictEqual( expect(safeDivideWithError(25, 0)).toStrictEqual(
Either.left(DivisionByZero), either.left(DivisionByZero),
); );
expect(safeDivideWithError(-25, 0)).toStrictEqual( expect(safeDivideWithError(-25, 0)).toStrictEqual(
Either.left(DivisionByZero), either.left(DivisionByZero),
); );
}); });
}); });
@ -65,15 +63,15 @@ describe('exo1', () => {
it('should eventually return the result of dividing two numbers', async () => { it('should eventually return the result of dividing two numbers', async () => {
const result = await asyncSafeDivideWithError(25, 5)(); const result = await asyncSafeDivideWithError(25, 5)();
expect(result).toStrictEqual(Either.right(5)); expect(result).toStrictEqual(either.right(5));
}); });
it('should eventually return Either.left(DivisionByZero) if the denominator is zero', async () => { it('should eventually return either.left(DivisionByZero) if the denominator is zero', async () => {
const resultA = await asyncSafeDivideWithError(25, 0)(); const resultA = await asyncSafeDivideWithError(25, 0)();
const resultB = await asyncSafeDivideWithError(-25, 0)(); const resultB = await asyncSafeDivideWithError(-25, 0)();
expect(resultA).toStrictEqual(Either.left(DivisionByZero)); expect(resultA).toStrictEqual(either.left(DivisionByZero));
expect(resultB).toStrictEqual(Either.left(DivisionByZero)); expect(resultB).toStrictEqual(either.left(DivisionByZero));
}); });
}); });
}); });

View file

@ -4,10 +4,9 @@
// - Either // - Either
// - TaskEither // - TaskEither
import * as Option from 'fp-ts/lib/Option'; import { Either } from 'fp-ts/Either';
import * as Either from 'fp-ts/lib/Either'; import { Option } from 'fp-ts/Option';
import * as TaskEither from 'fp-ts/lib/TaskEither'; import { TaskEither } from 'fp-ts/TaskEither';
import { unimplemented, sleep, unimplementedAsync } from '../utils'; import { unimplemented, sleep, unimplementedAsync } from '../utils';
export const divide = (a: number, b: number): number => { export const divide = (a: number, b: number): number => {
@ -22,13 +21,11 @@ export const divide = (a: number, b: number): number => {
// 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 contructors:
// - `Option.some(value)` // - `option.some(value)`
// - `Option.none` // - `option.none`
export const safeDivide: ( export const safeDivide: (a: number, b: number) => Option<number> =
a: number, unimplemented;
b: number,
) => Option.Option<number> = unimplemented;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// EITHER // // EITHER //
@ -40,10 +37,10 @@ export const safeDivide: (
// BONUS POINT: Implement `safeDivideWithError` in terms of `safeDivide`. // BONUS POINT: Implement `safeDivideWithError` in terms of `safeDivide`.
// //
// HINT : Either has two basic constructors: // HINT : Either has two basic constructors:
// - `Either.left(leftValue)` // - `either.left(leftValue)`
// - `Either.right(rightValue)` // - `either.right(rightValue)`
// as well as "smarter" constructors like: // as well as "smarter" constructors like:
// - `Either.fromOption(() => leftValue)(option)` // - `either.fromOption(() => leftValue)(option)`
// Here is an simple error type to help you: // Here is an simple error type to help you:
export type DivisionByZeroError = 'Error: Division by zero'; export type DivisionByZeroError = 'Error: Division by zero';
@ -52,7 +49,7 @@ export const DivisionByZero = 'Error: Division by zero' as const;
export const safeDivideWithError: ( export const safeDivideWithError: (
a: number, a: number,
b: number, b: number,
) => Either.Either<DivisionByZeroError, number> = unimplemented; ) => Either<DivisionByZeroError, number> = unimplemented;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// TASKEITHER // // TASKEITHER //
@ -75,9 +72,9 @@ export const asyncDivide = async (a: number, b: number) => {
// //
// HINT: TaskEither has a special constructor to transform a Promise<T> into // HINT: TaskEither has a special constructor to transform a Promise<T> into
// a TaskEither<Error, T>: // a TaskEither<Error, T>:
// - `TaskEither.tryCatch(f: () => promise, onReject: reason => leftValue)` // - `taskEither.tryCatch(f: () => promise, onReject: reason => leftValue)`
export const asyncSafeDivideWithError: ( export const asyncSafeDivideWithError: (
a: number, a: number,
b: number, b: number,
) => TaskEither.TaskEither<DivisionByZeroError, number> = unimplementedAsync; ) => TaskEither<DivisionByZeroError, number> = unimplementedAsync;

View file

@ -1,6 +1,4 @@
import * as Either from 'fp-ts/lib/Either'; import { either, option } from 'fp-ts';
import * as Option from 'fp-ts/lib/Option';
import { import {
Warrior, Warrior,
Wizard, Wizard,
@ -20,8 +18,8 @@ import {
describe('exo2', () => { describe('exo2', () => {
describe('checkTargetAndSmash', () => { describe('checkTargetAndSmash', () => {
it('should return a NoTarget error if no unit is selected', () => { it('should return a NoTarget error if no unit is selected', () => {
const result = checkTargetAndSmash(Option.none); const result = checkTargetAndSmash(option.none);
const expected = Either.left( const expected = either.left(
noTargetFailure('No unit currently selected'), noTargetFailure('No unit currently selected'),
); );
@ -30,14 +28,14 @@ describe('exo2', () => {
it('should return an InvalidTarget error if the wrong unit is selected', () => { it('should return an InvalidTarget error if the wrong unit is selected', () => {
const archer = new Archer(); const archer = new Archer();
const resultArcher = checkTargetAndSmash(Option.some(archer)); const resultArcher = checkTargetAndSmash(option.some(archer));
const expectedArcher = Either.left( const expectedArcher = either.left(
invalidTargetFailure('Archer cannot perform smash'), invalidTargetFailure('Archer cannot perform smash'),
); );
const wizard = new Wizard(); const wizard = new Wizard();
const resultWizard = checkTargetAndSmash(Option.some(wizard)); const resultWizard = checkTargetAndSmash(option.some(wizard));
const expectedWizard = Either.left( const expectedWizard = either.left(
invalidTargetFailure('Wizard cannot perform smash'), invalidTargetFailure('Wizard cannot perform smash'),
); );
@ -48,8 +46,8 @@ describe('exo2', () => {
it('should return the proper type of damage', () => { it('should return the proper type of damage', () => {
const warrior = new Warrior(); const warrior = new Warrior();
const result = checkTargetAndSmash(Option.some(warrior)); const result = checkTargetAndSmash(option.some(warrior));
const expected = Either.right(Damage.Physical); const expected = either.right(Damage.Physical);
expect(result).toStrictEqual(expected); expect(result).toStrictEqual(expected);
}); });
@ -57,8 +55,8 @@ describe('exo2', () => {
describe('checkTargetAndBurn', () => { describe('checkTargetAndBurn', () => {
it('should return a NoTarget error if no unit is selected', () => { it('should return a NoTarget error if no unit is selected', () => {
const result = checkTargetAndBurn(Option.none); const result = checkTargetAndBurn(option.none);
const expected = Either.left( const expected = either.left(
noTargetFailure('No unit currently selected'), noTargetFailure('No unit currently selected'),
); );
@ -67,14 +65,14 @@ describe('exo2', () => {
it('should return an InvalidTarget error if the wrong unit is selected', () => { it('should return an InvalidTarget error if the wrong unit is selected', () => {
const warrior = new Warrior(); const warrior = new Warrior();
const resultWarrior = checkTargetAndBurn(Option.some(warrior)); const resultWarrior = checkTargetAndBurn(option.some(warrior));
const expectedWarrior = Either.left( const expectedWarrior = either.left(
invalidTargetFailure('Warrior cannot perform burn'), invalidTargetFailure('Warrior cannot perform burn'),
); );
const archer = new Archer(); const archer = new Archer();
const resultArcher = checkTargetAndBurn(Option.some(archer)); const resultArcher = checkTargetAndBurn(option.some(archer));
const expectedArcher = Either.left( const expectedArcher = either.left(
invalidTargetFailure('Archer cannot perform burn'), invalidTargetFailure('Archer cannot perform burn'),
); );
@ -85,8 +83,8 @@ describe('exo2', () => {
it('should return the proper type of damage', () => { it('should return the proper type of damage', () => {
const wizard = new Wizard(); const wizard = new Wizard();
const result = checkTargetAndBurn(Option.some(wizard)); const result = checkTargetAndBurn(option.some(wizard));
const expected = Either.right(Damage.Magical); const expected = either.right(Damage.Magical);
expect(result).toStrictEqual(expected); expect(result).toStrictEqual(expected);
}); });
@ -94,8 +92,8 @@ describe('exo2', () => {
describe('checkTargetAndShoot', () => { describe('checkTargetAndShoot', () => {
it('should return a NoTarget error if no unit is selected', () => { it('should return a NoTarget error if no unit is selected', () => {
const result = checkTargetAndShoot(Option.none); const result = checkTargetAndShoot(option.none);
const expected = Either.left( const expected = either.left(
noTargetFailure('No unit currently selected'), noTargetFailure('No unit currently selected'),
); );
@ -104,14 +102,14 @@ describe('exo2', () => {
it('should return an InvalidTarget error if the wrong unit is selected', () => { it('should return an InvalidTarget error if the wrong unit is selected', () => {
const warrior = new Warrior(); const warrior = new Warrior();
const resultWarrior = checkTargetAndShoot(Option.some(warrior)); const resultWarrior = checkTargetAndShoot(option.some(warrior));
const expectedWarrior = Either.left( const expectedWarrior = either.left(
invalidTargetFailure('Warrior cannot perform shoot'), invalidTargetFailure('Warrior cannot perform shoot'),
); );
const wizard = new Wizard(); const wizard = new Wizard();
const resultWizard = checkTargetAndShoot(Option.some(wizard)); const resultWizard = checkTargetAndShoot(option.some(wizard));
const expectedWizard = Either.left( const expectedWizard = either.left(
invalidTargetFailure('Wizard cannot perform shoot'), invalidTargetFailure('Wizard cannot perform shoot'),
); );
@ -122,80 +120,80 @@ describe('exo2', () => {
it('should return the proper type of damage', () => { it('should return the proper type of damage', () => {
const archer = new Archer(); const archer = new Archer();
const result = checkTargetAndShoot(Option.some(archer)); const result = checkTargetAndShoot(option.some(archer));
const expected = Either.right(Damage.Ranged); const expected = either.right(Damage.Ranged);
expect(result).toStrictEqual(expected); expect(result).toStrictEqual(expected);
}); });
}); });
describe('smashOption', () => { describe('smashOption', () => {
it('should return Option.none if the character is of the wrong type', () => { it('should return option.none if the character is of the wrong type', () => {
const wizard = new Wizard(); const wizard = new Wizard();
const archer = new Archer(); const archer = new Archer();
const resultWizard = smashOption(wizard); const resultWizard = smashOption(wizard);
const resultArcher = smashOption(archer); const resultArcher = smashOption(archer);
const expected = Option.none; const expected = option.none;
expect(resultWizard).toStrictEqual(expected); expect(resultWizard).toStrictEqual(expected);
expect(resultArcher).toStrictEqual(expected); expect(resultArcher).toStrictEqual(expected);
}); });
it('should return Option.some(Damage.Physical) if the character is a warrior', () => { it('should return option.some(Damage.Physical) if the character is a warrior', () => {
const warrior = new Warrior(); const warrior = new Warrior();
const result = smashOption(warrior); const result = smashOption(warrior);
const expected = Option.some(Damage.Physical); const expected = option.some(Damage.Physical);
expect(result).toStrictEqual(expected); expect(result).toStrictEqual(expected);
}); });
}); });
describe('burnOption', () => { describe('burnOption', () => {
it('should return Option.none if the character is of the wrong type', () => { it('should return option.none if the character is of the wrong type', () => {
const warrior = new Warrior(); const warrior = new Warrior();
const archer = new Archer(); const archer = new Archer();
const resultWarrior = burnOption(warrior); const resultWarrior = burnOption(warrior);
const resultArcher = burnOption(archer); const resultArcher = burnOption(archer);
const expected = Option.none; const expected = option.none;
expect(resultWarrior).toStrictEqual(expected); expect(resultWarrior).toStrictEqual(expected);
expect(resultArcher).toStrictEqual(expected); expect(resultArcher).toStrictEqual(expected);
}); });
it('should return Option.some(Damage.Magical) if the character is a wizard', () => { it('should return option.some(Damage.Magical) if the character is a wizard', () => {
const wizard = new Wizard(); const wizard = new Wizard();
const result = burnOption(wizard); const result = burnOption(wizard);
const expected = Option.some(Damage.Magical); const expected = option.some(Damage.Magical);
expect(result).toStrictEqual(expected); expect(result).toStrictEqual(expected);
}); });
}); });
describe('shootOption', () => { describe('shootOption', () => {
it('should return Option.none if the character is of the wrong type', () => { it('should return option.none if the character is of the wrong type', () => {
const warrior = new Warrior(); const warrior = new Warrior();
const wizard = new Wizard(); const wizard = new Wizard();
const resultWizard = shootOption(wizard); const resultWizard = shootOption(wizard);
const resultWarrior = shootOption(warrior); const resultWarrior = shootOption(warrior);
const expected = Option.none; const expected = option.none;
expect(resultWarrior).toStrictEqual(expected); expect(resultWarrior).toStrictEqual(expected);
expect(resultWizard).toStrictEqual(expected); expect(resultWizard).toStrictEqual(expected);
}); });
it('should return Option.some(Damage.Ranged) if the character is an archer', () => { it('should return option.some(Damage.Ranged) if the character is an archer', () => {
const archer = new Archer(); const archer = new Archer();
const result = shootOption(archer); const result = shootOption(archer);
const expected = Option.some(Damage.Ranged); const expected = option.some(Damage.Ranged);
expect(result).toStrictEqual(expected); expect(result).toStrictEqual(expected);
}); });

View file

@ -1,9 +1,8 @@
// `fp-ts` training Exercice 2 // `fp-ts` training Exercice 2
// Let's have fun with combinators! // Let's have fun with combinators!
import * as Option from 'fp-ts/lib/Option'; import { Either } from 'fp-ts/Either';
import * as Either from 'fp-ts/lib/Either'; import { Option } from 'fp-ts/Option';
import { Failure } from '../Failure'; import { Failure } from '../Failure';
import { unimplemented } from '../utils'; import { unimplemented } from '../utils';
@ -100,12 +99,12 @@ export const invalidTargetFailure = Failure.builder(
// return the expected damage type if appropriate. // return the expected damage type if appropriate.
// //
// If no unit is selected, it should return // If no unit is selected, it should return
// `Either.left(noTargetFailure('No unit currently selected'))` // `either.left(noTargetFailure('No unit currently selected'))`
// //
// If a unit of the wrong type is selected, it should return // If a unit of the wrong type is selected, it should return
// `Either.left(invalidTargetFailure('<unit_type> cannot perform <action>'))` // `either.left(invalidTargetFailure('<unit_type> cannot perform <action>'))`
// //
// Otherwise, it should return `Either.right(<expected_damage_type>)` // Otherwise, it should return `either.right(<expected_damage_type>)`
// //
// HINT: These functions represent the public API. But it is heavily // HINT: These functions represent the public API. But it is heavily
// recommended to break those down into smaller private functions that can be // recommended to break those down into smaller private functions that can be
@ -119,25 +118,16 @@ export const invalidTargetFailure = Failure.builder(
// the `chain` operator and its slightly relaxed variant `chainW`. // the `chain` operator and its slightly relaxed variant `chainW`.
export const checkTargetAndSmash: ( export const checkTargetAndSmash: (
target: Option.Option<Character>, target: Option<Character>,
) => Either.Either< ) => Either<NoTargetFailure | InvalidTargetFailure, Damage> = unimplemented;
NoTargetFailure | InvalidTargetFailure,
Damage
> = unimplemented;
export const checkTargetAndBurn: ( export const checkTargetAndBurn: (
target: Option.Option<Character>, target: Option<Character>,
) => Either.Either< ) => Either<NoTargetFailure | InvalidTargetFailure, Damage> = unimplemented;
NoTargetFailure | InvalidTargetFailure,
Damage
> = unimplemented;
export const checkTargetAndShoot: ( export const checkTargetAndShoot: (
target: Option.Option<Character>, target: Option<Character>,
) => Either.Either< ) => Either<NoTargetFailure | InvalidTargetFailure, Damage> = unimplemented;
NoTargetFailure | InvalidTargetFailure,
Damage
> = unimplemented;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// OPTION // // OPTION //
@ -156,17 +146,14 @@ export const checkTargetAndShoot: (
// BONUS POINTS: If you properly defined small private helpers in the previous // BONUS POINTS: If you properly defined small private helpers in the previous
// section, they should be easily reused for those use-cases. // section, they should be easily reused for those use-cases.
export const smashOption: ( export const smashOption: (character: Character) => Option<Damage> =
character: Character, unimplemented;
) => Option.Option<Damage> = unimplemented;
export const burnOption: ( export const burnOption: (character: Character) => Option<Damage> =
character: Character, unimplemented;
) => Option.Option<Damage> = unimplemented;
export const shootOption: ( export const shootOption: (character: Character) => Option<Damage> =
character: Character, unimplemented;
) => Option.Option<Damage> = unimplemented;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// ARRAY // // ARRAY //
@ -187,6 +174,5 @@ export interface TotalDamage {
[Damage.Ranged]: number; [Damage.Ranged]: number;
} }
export const attack: ( export const attack: (army: ReadonlyArray<Character>) => TotalDamage =
army: ReadonlyArray<Character>, unimplemented;
) => TotalDamage = unimplemented;

View file

@ -1,5 +1,4 @@
import * as Option from 'fp-ts/lib/Option'; import { option } from 'fp-ts';
import { import {
sortStrings, sortStrings,
sortNumbers, sortNumbers,
@ -46,10 +45,10 @@ describe('exo3', () => {
describe('sortOptionalNumbers', () => { describe('sortOptionalNumbers', () => {
it('should return a sorted array of optional numbers', () => { it('should return a sorted array of optional numbers', () => {
const optionalNumbers = [Option.some(1337), Option.none, Option.some(42)]; const optionalNumbers = [option.some(1337), option.none, option.some(42)];
const result = sortOptionalNumbers(optionalNumbers); const result = sortOptionalNumbers(optionalNumbers);
const expected = [Option.none, Option.some(42), Option.some(1337)]; const expected = [option.none, option.some(42), option.some(1337)];
expect(result).toStrictEqual(expected); expect(result).toStrictEqual(expected);
}); });
@ -57,9 +56,9 @@ describe('exo3', () => {
describe('sortPersonsByName', () => { describe('sortPersonsByName', () => {
it('should return an array of persons alphabetically sorted by their name', () => { it('should return an array of persons alphabetically sorted by their name', () => {
const alice = { name: 'Alice', age: Option.none }; const alice = { name: 'Alice', age: option.none };
const bob = { name: 'Bob', age: Option.none }; const bob = { name: 'Bob', age: option.none };
const crystal = { name: 'Crystal', age: Option.none }; const crystal = { name: 'Crystal', age: option.none };
const persons = [crystal, alice, bob]; const persons = [crystal, alice, bob];
@ -72,9 +71,9 @@ describe('exo3', () => {
describe('sortPersonsByName', () => { describe('sortPersonsByName', () => {
it('should return an array of persons sorted by their age', () => { it('should return an array of persons sorted by their age', () => {
const alice = { name: 'Alice', age: Option.some(42) }; const alice = { name: 'Alice', age: option.some(42) };
const bob = { name: 'Bob', age: Option.none }; const bob = { name: 'Bob', age: option.none };
const crystal = { name: 'Crystal', age: Option.some(29) }; const crystal = { name: 'Crystal', age: option.some(29) };
const persons = [crystal, alice, bob]; const persons = [crystal, alice, bob];
@ -87,11 +86,11 @@ describe('exo3', () => {
describe('sortPersonsByName', () => { describe('sortPersonsByName', () => {
it('should return an array of persons sorted first by age and then by name', () => { it('should return an array of persons sorted first by age and then by name', () => {
const alice = { name: 'Alice', age: Option.some(42) }; const alice = { name: 'Alice', age: option.some(42) };
const bob = { name: 'Bob', age: Option.none }; const bob = { name: 'Bob', age: option.none };
const crystal = { name: 'Crystal', age: Option.some(29) }; const crystal = { name: 'Crystal', age: option.some(29) };
const dorian = { name: 'Dorian', age: Option.some(29) }; const dorian = { name: 'Dorian', age: option.some(29) };
const edgar = { name: 'Edgar', age: Option.none }; const edgar = { name: 'Edgar', age: option.none };
const persons = [dorian, alice, edgar, bob, crystal]; const persons = [dorian, alice, edgar, bob, crystal];

View file

@ -1,8 +1,7 @@
// `fp-ts` training Exercice 3 // `fp-ts` training Exercice 3
// Sort things out with `Ord` // Sort things out with `Ord`
import * as Option from 'fp-ts/lib/Option'; import { Option } from 'fp-ts/Option';
import { unimplemented } from '../utils'; import { unimplemented } from '../utils';
// Have you ever looked at the methods provided by `fp-ts` own `Array` and // Have you ever looked at the methods provided by `fp-ts` own `Array` and
@ -13,7 +12,7 @@ import { unimplemented } from '../utils';
// The difference with JavaScript's native `Array.prototype` methods is that // The difference with JavaScript's native `Array.prototype` methods is that
// these are more `fp-ts` friendly. // these are more `fp-ts` friendly.
// //
// In the following exercice, we will take a look at `Array.sort`. Contrary to // In the following exercice, we will take a look at `array.sort`. Contrary to
// its JavaScript counterpart, `fp-ts` sort takes as an argument something of // its JavaScript counterpart, `fp-ts` sort takes as an argument something of
// type `Ord<T>` where `T` is the type of elements contained in the collection. // type `Ord<T>` where `T` is the type of elements contained in the collection.
// //
@ -27,13 +26,13 @@ import { unimplemented } from '../utils';
// like `string` or `number` and return a new array with those values but // like `string` or `number` and return a new array with those values but
// sorted. // sorted.
// //
// Obviously, we want to call `ReadonlyArray.sort` (the `fp-ts` version! no // Obviously, we want to call `readonlyArray.sort` (the `fp-ts` version! no
// cheating). But, contrary to `ReadonlyArray.prototype.sort` which takes an // cheating). But, contrary to `ReadonlyArray.prototype.sort` which takes an
// ordering function, this sort will only accept an `Ord<T>`. // ordering function, this sort will only accept an `Ord<T>`.
// //
// HINT: The `Ord` module from `fp-ts` exposes some preconstructed instances // HINT: The primitive type modules from `fp-ts` (`number`, `string`...)
// of `Ord<T>` for a few primitive `T`s such as `ordString: Ord<string>` or // expose some preconstructed instances of `Ord<T>` for said primitives such as
// `ordNumber: Ord<number>`. // `string.Ord: Ord<string>` or `number.Ord: Ord<number>`.
export const sortStrings: ( export const sortStrings: (
strings: ReadonlyArray<string>, strings: ReadonlyArray<string>,
@ -48,12 +47,13 @@ export const sortNumbers: (
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// This next function will sort an array of numbers but in descending order // This next function will sort an array of numbers but in descending order
// (which unfortunately is the reverse ordering from the provided `ordNumber`). // (which unfortunately is the reverse ordering from the one provided by
// `number.Ord`).
// //
// Sure, we could just use `sortNumbers` defined earlier and then reverse the // Sure, we could just use `sortNumbers` defined earlier and then reverse the
// whole array but that would be horribly inefficient wouldn't it? // whole array but that would be horribly inefficient wouldn't it?
// //
// HINT: Any ordering can be reversed with a simple function `Ord.getDualOrd`. // HINT: Any ordering can be reversed with a simple function `ord.reverse`.
export const sortNumbersDescending: ( export const sortNumbersDescending: (
numbers: ReadonlyArray<number>, numbers: ReadonlyArray<number>,
@ -64,18 +64,18 @@ export const sortNumbersDescending: (
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// This next function will sort an array of numbers wrapped in `Option` with // This next function will sort an array of numbers wrapped in `Option` with
// the following constraint: `Option.none` < `Option.some(_)`. // the following constraint: `option.none` < `option.some(_)`.
// //
// As such, we cannot simply use `ordNumber` because it has type `Ord<number>` // As such, we cannot simply use `number.Ord` because it has type `Ord<number>`
// and we need an instance of `Ord<Option<number>>`. // and we need an instance of `Ord<Option<number>>`.
// //
// HINT: Some of `fp-ts` wrapper types such as `Option` do already have a way // HINT: Some of `fp-ts` wrapper types such as `Option` do already have a way
// of building an `Ord` instance for their qualified inner type. You may want // of building an `Ord` instance for their qualified inner type. You may want
// to take a look at `Option.getOrd`. // to take a look at `option.getOrd`.
export const sortOptionalNumbers: ( export const sortOptionalNumbers: (
optionalNumbers: ReadonlyArray<Option.Option<number>>, optionalNumbers: ReadonlyArray<Option<number>>,
) => ReadonlyArray<Option.Option<number>> = unimplemented; ) => ReadonlyArray<Option<number>> = unimplemented;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// SORT COMPLEX OBJECTS // // SORT COMPLEX OBJECTS //
@ -92,11 +92,11 @@ export const sortOptionalNumbers: (
// //
// HINT: You can build an instance of `Ord` specialized for a field for a // HINT: You can build an instance of `Ord` specialized for a field for a
// record with many fields by declaring how to access that field and which // record with many fields by declaring how to access that field and which
// primitive `Ord` instance to use. This can be achieved with `Ord.contramap`. // primitive `Ord` instance to use. This can be achieved with `ord.contramap`.
export interface Person { export interface Person {
readonly name: string; readonly name: string;
readonly age: Option.Option<number>; readonly age: Option<number>;
} }
export const sortPersonsByName: ( export const sortPersonsByName: (
@ -114,7 +114,7 @@ export const sortPersonsByAge: (
// Now, we want to sort the array first by age, but for people of the same age, // Now, we want to sort the array first by age, but for people of the same age,
// we want to sort them by name. // we want to sort them by name.
// //
// HINT: Take a look at `ReadonlyArray.sortBy` // HINT: Take a look at `readonlyArray.sortBy`
export const sortPersonsByAgeThenByName: ( export const sortPersonsByAgeThenByName: (
person: ReadonlyArray<Person>, person: ReadonlyArray<Person>,