🎨 Switch to the preferred way of importing fp-ts
This commit is contained in:
parent
9102b3da54
commit
53440267bd
7 changed files with 116 additions and 148 deletions
|
@ -1,8 +1,5 @@
|
|||
import * as decod from 'decod';
|
||||
import * as Either from 'fp-ts/lib/Either';
|
||||
import * as TaskEither from 'fp-ts/lib/TaskEither';
|
||||
import * as Task from 'fp-ts/lib/Task';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { either, task } from 'fp-ts';
|
||||
|
||||
const decodeErrorMessage = decod.at('message', decod.string);
|
||||
|
||||
|
@ -70,16 +67,9 @@ export class Failure<T extends string = FailureType> {
|
|||
throw failure.toError();
|
||||
};
|
||||
|
||||
static eitherUnsafeGet = <A>(either: Either.Either<Failure, A>): A =>
|
||||
pipe(either, Either.getOrElseW(Failure.throw));
|
||||
static eitherUnsafeGet = either.getOrElseW(Failure.throw);
|
||||
|
||||
static taskEitherUnsafeGet = <A>(
|
||||
taskEither: TaskEither.TaskEither<Failure, A>,
|
||||
): Task.Task<A> =>
|
||||
pipe(
|
||||
taskEither,
|
||||
TaskEither.getOrElseW((failure) => async () => Failure.throw(failure)),
|
||||
);
|
||||
static taskEitherUnsafeGet = task.map(this.eitherUnsafeGet);
|
||||
}
|
||||
|
||||
export type FailureTypes<F extends Record<string, string>> = F[keyof F];
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import * as Either from 'fp-ts/lib/Either';
|
||||
import * as Option from 'fp-ts/lib/Option';
|
||||
|
||||
import { either, option } from 'fp-ts';
|
||||
import {
|
||||
divide,
|
||||
DivisionByZero,
|
||||
|
@ -24,26 +22,26 @@ describe('exo1', () => {
|
|||
|
||||
describe('safeDivide', () => {
|
||||
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', () => {
|
||||
expect(safeDivide(25, 0)).toStrictEqual(Option.none);
|
||||
expect(safeDivide(-25, 0)).toStrictEqual(Option.none);
|
||||
it('should return option.none if the denominator is zero', () => {
|
||||
expect(safeDivide(25, 0)).toStrictEqual(option.none);
|
||||
expect(safeDivide(-25, 0)).toStrictEqual(option.none);
|
||||
});
|
||||
});
|
||||
|
||||
describe('safeDivideWithError', () => {
|
||||
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(
|
||||
Either.left(DivisionByZero),
|
||||
either.left(DivisionByZero),
|
||||
);
|
||||
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 () => {
|
||||
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 resultB = await asyncSafeDivideWithError(-25, 0)();
|
||||
|
||||
expect(resultA).toStrictEqual(Either.left(DivisionByZero));
|
||||
expect(resultB).toStrictEqual(Either.left(DivisionByZero));
|
||||
expect(resultA).toStrictEqual(either.left(DivisionByZero));
|
||||
expect(resultB).toStrictEqual(either.left(DivisionByZero));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
// - Either
|
||||
// - TaskEither
|
||||
|
||||
import * as Option from 'fp-ts/lib/Option';
|
||||
import * as Either from 'fp-ts/lib/Either';
|
||||
import * as TaskEither from 'fp-ts/lib/TaskEither';
|
||||
|
||||
import { Either } from 'fp-ts/Either';
|
||||
import { Option } from 'fp-ts/Option';
|
||||
import { TaskEither } from 'fp-ts/TaskEither';
|
||||
import { unimplemented, sleep, unimplementedAsync } from '../utils';
|
||||
|
||||
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>
|
||||
//
|
||||
// HINT: Option has two basic contructors:
|
||||
// - `Option.some(value)`
|
||||
// - `Option.none`
|
||||
// - `option.some(value)`
|
||||
// - `option.none`
|
||||
|
||||
export const safeDivide: (
|
||||
a: number,
|
||||
b: number,
|
||||
) => Option.Option<number> = unimplemented;
|
||||
export const safeDivide: (a: number, b: number) => Option<number> =
|
||||
unimplemented;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EITHER //
|
||||
|
@ -40,10 +37,10 @@ export const safeDivide: (
|
|||
// BONUS POINT: Implement `safeDivideWithError` in terms of `safeDivide`.
|
||||
//
|
||||
// HINT : Either has two basic constructors:
|
||||
// - `Either.left(leftValue)`
|
||||
// - `Either.right(rightValue)`
|
||||
// - `either.left(leftValue)`
|
||||
// - `either.right(rightValue)`
|
||||
// as well as "smarter" constructors like:
|
||||
// - `Either.fromOption(() => leftValue)(option)`
|
||||
// - `either.fromOption(() => leftValue)(option)`
|
||||
|
||||
// Here is an simple error type to help you:
|
||||
export type DivisionByZeroError = 'Error: Division by zero';
|
||||
|
@ -52,7 +49,7 @@ export const DivisionByZero = 'Error: Division by zero' as const;
|
|||
export const safeDivideWithError: (
|
||||
a: number,
|
||||
b: number,
|
||||
) => Either.Either<DivisionByZeroError, number> = unimplemented;
|
||||
) => Either<DivisionByZeroError, number> = unimplemented;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
// a TaskEither<Error, T>:
|
||||
// - `TaskEither.tryCatch(f: () => promise, onReject: reason => leftValue)`
|
||||
// - `taskEither.tryCatch(f: () => promise, onReject: reason => leftValue)`
|
||||
|
||||
export const asyncSafeDivideWithError: (
|
||||
a: number,
|
||||
b: number,
|
||||
) => TaskEither.TaskEither<DivisionByZeroError, number> = unimplementedAsync;
|
||||
) => TaskEither<DivisionByZeroError, number> = unimplementedAsync;
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import * as Either from 'fp-ts/lib/Either';
|
||||
import * as Option from 'fp-ts/lib/Option';
|
||||
|
||||
import { either, option } from 'fp-ts';
|
||||
import {
|
||||
Warrior,
|
||||
Wizard,
|
||||
|
@ -20,8 +18,8 @@ import {
|
|||
describe('exo2', () => {
|
||||
describe('checkTargetAndSmash', () => {
|
||||
it('should return a NoTarget error if no unit is selected', () => {
|
||||
const result = checkTargetAndSmash(Option.none);
|
||||
const expected = Either.left(
|
||||
const result = checkTargetAndSmash(option.none);
|
||||
const expected = either.left(
|
||||
noTargetFailure('No unit currently selected'),
|
||||
);
|
||||
|
||||
|
@ -30,14 +28,14 @@ describe('exo2', () => {
|
|||
|
||||
it('should return an InvalidTarget error if the wrong unit is selected', () => {
|
||||
const archer = new Archer();
|
||||
const resultArcher = checkTargetAndSmash(Option.some(archer));
|
||||
const expectedArcher = Either.left(
|
||||
const resultArcher = checkTargetAndSmash(option.some(archer));
|
||||
const expectedArcher = either.left(
|
||||
invalidTargetFailure('Archer cannot perform smash'),
|
||||
);
|
||||
|
||||
const wizard = new Wizard();
|
||||
const resultWizard = checkTargetAndSmash(Option.some(wizard));
|
||||
const expectedWizard = Either.left(
|
||||
const resultWizard = checkTargetAndSmash(option.some(wizard));
|
||||
const expectedWizard = either.left(
|
||||
invalidTargetFailure('Wizard cannot perform smash'),
|
||||
);
|
||||
|
||||
|
@ -48,8 +46,8 @@ describe('exo2', () => {
|
|||
|
||||
it('should return the proper type of damage', () => {
|
||||
const warrior = new Warrior();
|
||||
const result = checkTargetAndSmash(Option.some(warrior));
|
||||
const expected = Either.right(Damage.Physical);
|
||||
const result = checkTargetAndSmash(option.some(warrior));
|
||||
const expected = either.right(Damage.Physical);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
@ -57,8 +55,8 @@ describe('exo2', () => {
|
|||
|
||||
describe('checkTargetAndBurn', () => {
|
||||
it('should return a NoTarget error if no unit is selected', () => {
|
||||
const result = checkTargetAndBurn(Option.none);
|
||||
const expected = Either.left(
|
||||
const result = checkTargetAndBurn(option.none);
|
||||
const expected = either.left(
|
||||
noTargetFailure('No unit currently selected'),
|
||||
);
|
||||
|
||||
|
@ -67,14 +65,14 @@ describe('exo2', () => {
|
|||
|
||||
it('should return an InvalidTarget error if the wrong unit is selected', () => {
|
||||
const warrior = new Warrior();
|
||||
const resultWarrior = checkTargetAndBurn(Option.some(warrior));
|
||||
const expectedWarrior = Either.left(
|
||||
const resultWarrior = checkTargetAndBurn(option.some(warrior));
|
||||
const expectedWarrior = either.left(
|
||||
invalidTargetFailure('Warrior cannot perform burn'),
|
||||
);
|
||||
|
||||
const archer = new Archer();
|
||||
const resultArcher = checkTargetAndBurn(Option.some(archer));
|
||||
const expectedArcher = Either.left(
|
||||
const resultArcher = checkTargetAndBurn(option.some(archer));
|
||||
const expectedArcher = either.left(
|
||||
invalidTargetFailure('Archer cannot perform burn'),
|
||||
);
|
||||
|
||||
|
@ -85,8 +83,8 @@ describe('exo2', () => {
|
|||
|
||||
it('should return the proper type of damage', () => {
|
||||
const wizard = new Wizard();
|
||||
const result = checkTargetAndBurn(Option.some(wizard));
|
||||
const expected = Either.right(Damage.Magical);
|
||||
const result = checkTargetAndBurn(option.some(wizard));
|
||||
const expected = either.right(Damage.Magical);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
@ -94,8 +92,8 @@ describe('exo2', () => {
|
|||
|
||||
describe('checkTargetAndShoot', () => {
|
||||
it('should return a NoTarget error if no unit is selected', () => {
|
||||
const result = checkTargetAndShoot(Option.none);
|
||||
const expected = Either.left(
|
||||
const result = checkTargetAndShoot(option.none);
|
||||
const expected = either.left(
|
||||
noTargetFailure('No unit currently selected'),
|
||||
);
|
||||
|
||||
|
@ -104,14 +102,14 @@ describe('exo2', () => {
|
|||
|
||||
it('should return an InvalidTarget error if the wrong unit is selected', () => {
|
||||
const warrior = new Warrior();
|
||||
const resultWarrior = checkTargetAndShoot(Option.some(warrior));
|
||||
const expectedWarrior = Either.left(
|
||||
const resultWarrior = checkTargetAndShoot(option.some(warrior));
|
||||
const expectedWarrior = either.left(
|
||||
invalidTargetFailure('Warrior cannot perform shoot'),
|
||||
);
|
||||
|
||||
const wizard = new Wizard();
|
||||
const resultWizard = checkTargetAndShoot(Option.some(wizard));
|
||||
const expectedWizard = Either.left(
|
||||
const resultWizard = checkTargetAndShoot(option.some(wizard));
|
||||
const expectedWizard = either.left(
|
||||
invalidTargetFailure('Wizard cannot perform shoot'),
|
||||
);
|
||||
|
||||
|
@ -122,80 +120,80 @@ describe('exo2', () => {
|
|||
|
||||
it('should return the proper type of damage', () => {
|
||||
const archer = new Archer();
|
||||
const result = checkTargetAndShoot(Option.some(archer));
|
||||
const expected = Either.right(Damage.Ranged);
|
||||
const result = checkTargetAndShoot(option.some(archer));
|
||||
const expected = either.right(Damage.Ranged);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
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 archer = new Archer();
|
||||
|
||||
const resultWizard = smashOption(wizard);
|
||||
const resultArcher = smashOption(archer);
|
||||
|
||||
const expected = Option.none;
|
||||
const expected = option.none;
|
||||
|
||||
expect(resultWizard).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 result = smashOption(warrior);
|
||||
const expected = Option.some(Damage.Physical);
|
||||
const expected = option.some(Damage.Physical);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
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 archer = new Archer();
|
||||
|
||||
const resultWarrior = burnOption(warrior);
|
||||
const resultArcher = burnOption(archer);
|
||||
|
||||
const expected = Option.none;
|
||||
const expected = option.none;
|
||||
|
||||
expect(resultWarrior).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 result = burnOption(wizard);
|
||||
const expected = Option.some(Damage.Magical);
|
||||
const expected = option.some(Damage.Magical);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
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 wizard = new Wizard();
|
||||
|
||||
const resultWizard = shootOption(wizard);
|
||||
const resultWarrior = shootOption(warrior);
|
||||
|
||||
const expected = Option.none;
|
||||
const expected = option.none;
|
||||
|
||||
expect(resultWarrior).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 result = shootOption(archer);
|
||||
const expected = Option.some(Damage.Ranged);
|
||||
const expected = option.some(Damage.Ranged);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// `fp-ts` training Exercice 2
|
||||
// Let's have fun with combinators!
|
||||
|
||||
import * as Option from 'fp-ts/lib/Option';
|
||||
import * as Either from 'fp-ts/lib/Either';
|
||||
|
||||
import { Either } from 'fp-ts/Either';
|
||||
import { Option } from 'fp-ts/Option';
|
||||
import { Failure } from '../Failure';
|
||||
import { unimplemented } from '../utils';
|
||||
|
||||
|
@ -100,12 +99,12 @@ export const invalidTargetFailure = Failure.builder(
|
|||
// return the expected damage type if appropriate.
|
||||
//
|
||||
// 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
|
||||
// `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
|
||||
// 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`.
|
||||
|
||||
export const checkTargetAndSmash: (
|
||||
target: Option.Option<Character>,
|
||||
) => Either.Either<
|
||||
NoTargetFailure | InvalidTargetFailure,
|
||||
Damage
|
||||
> = unimplemented;
|
||||
target: Option<Character>,
|
||||
) => Either<NoTargetFailure | InvalidTargetFailure, Damage> = unimplemented;
|
||||
|
||||
export const checkTargetAndBurn: (
|
||||
target: Option.Option<Character>,
|
||||
) => Either.Either<
|
||||
NoTargetFailure | InvalidTargetFailure,
|
||||
Damage
|
||||
> = unimplemented;
|
||||
target: Option<Character>,
|
||||
) => Either<NoTargetFailure | InvalidTargetFailure, Damage> = unimplemented;
|
||||
|
||||
export const checkTargetAndShoot: (
|
||||
target: Option.Option<Character>,
|
||||
) => Either.Either<
|
||||
NoTargetFailure | InvalidTargetFailure,
|
||||
Damage
|
||||
> = unimplemented;
|
||||
target: Option<Character>,
|
||||
) => Either<NoTargetFailure | InvalidTargetFailure, Damage> = unimplemented;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// OPTION //
|
||||
|
@ -156,17 +146,14 @@ export const checkTargetAndShoot: (
|
|||
// BONUS POINTS: If you properly defined small private helpers in the previous
|
||||
// section, they should be easily reused for those use-cases.
|
||||
|
||||
export const smashOption: (
|
||||
character: Character,
|
||||
) => Option.Option<Damage> = unimplemented;
|
||||
export const smashOption: (character: Character) => Option<Damage> =
|
||||
unimplemented;
|
||||
|
||||
export const burnOption: (
|
||||
character: Character,
|
||||
) => Option.Option<Damage> = unimplemented;
|
||||
export const burnOption: (character: Character) => Option<Damage> =
|
||||
unimplemented;
|
||||
|
||||
export const shootOption: (
|
||||
character: Character,
|
||||
) => Option.Option<Damage> = unimplemented;
|
||||
export const shootOption: (character: Character) => Option<Damage> =
|
||||
unimplemented;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ARRAY //
|
||||
|
@ -187,6 +174,5 @@ export interface TotalDamage {
|
|||
[Damage.Ranged]: number;
|
||||
}
|
||||
|
||||
export const attack: (
|
||||
army: ReadonlyArray<Character>,
|
||||
) => TotalDamage = unimplemented;
|
||||
export const attack: (army: ReadonlyArray<Character>) => TotalDamage =
|
||||
unimplemented;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as Option from 'fp-ts/lib/Option';
|
||||
|
||||
import { option } from 'fp-ts';
|
||||
import {
|
||||
sortStrings,
|
||||
sortNumbers,
|
||||
|
@ -46,10 +45,10 @@ describe('exo3', () => {
|
|||
|
||||
describe('sortOptionalNumbers', () => {
|
||||
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 expected = [Option.none, Option.some(42), Option.some(1337)];
|
||||
const expected = [option.none, option.some(42), option.some(1337)];
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
@ -57,9 +56,9 @@ describe('exo3', () => {
|
|||
|
||||
describe('sortPersonsByName', () => {
|
||||
it('should return an array of persons alphabetically sorted by their name', () => {
|
||||
const alice = { name: 'Alice', age: Option.none };
|
||||
const bob = { name: 'Bob', age: Option.none };
|
||||
const crystal = { name: 'Crystal', age: Option.none };
|
||||
const alice = { name: 'Alice', age: option.none };
|
||||
const bob = { name: 'Bob', age: option.none };
|
||||
const crystal = { name: 'Crystal', age: option.none };
|
||||
|
||||
const persons = [crystal, alice, bob];
|
||||
|
||||
|
@ -72,9 +71,9 @@ describe('exo3', () => {
|
|||
|
||||
describe('sortPersonsByName', () => {
|
||||
it('should return an array of persons sorted by their age', () => {
|
||||
const alice = { name: 'Alice', age: Option.some(42) };
|
||||
const bob = { name: 'Bob', age: Option.none };
|
||||
const crystal = { name: 'Crystal', age: Option.some(29) };
|
||||
const alice = { name: 'Alice', age: option.some(42) };
|
||||
const bob = { name: 'Bob', age: option.none };
|
||||
const crystal = { name: 'Crystal', age: option.some(29) };
|
||||
|
||||
const persons = [crystal, alice, bob];
|
||||
|
||||
|
@ -87,11 +86,11 @@ describe('exo3', () => {
|
|||
|
||||
describe('sortPersonsByName', () => {
|
||||
it('should return an array of persons sorted first by age and then by name', () => {
|
||||
const alice = { name: 'Alice', age: Option.some(42) };
|
||||
const bob = { name: 'Bob', age: Option.none };
|
||||
const crystal = { name: 'Crystal', age: Option.some(29) };
|
||||
const dorian = { name: 'Dorian', age: Option.some(29) };
|
||||
const edgar = { name: 'Edgar', age: Option.none };
|
||||
const alice = { name: 'Alice', age: option.some(42) };
|
||||
const bob = { name: 'Bob', age: option.none };
|
||||
const crystal = { name: 'Crystal', age: option.some(29) };
|
||||
const dorian = { name: 'Dorian', age: option.some(29) };
|
||||
const edgar = { name: 'Edgar', age: option.none };
|
||||
|
||||
const persons = [dorian, alice, edgar, bob, crystal];
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// `fp-ts` training Exercice 3
|
||||
// Sort things out with `Ord`
|
||||
|
||||
import * as Option from 'fp-ts/lib/Option';
|
||||
|
||||
import { Option } from 'fp-ts/Option';
|
||||
import { unimplemented } from '../utils';
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
// ordering function, this sort will only accept an `Ord<T>`.
|
||||
//
|
||||
// HINT: The `Ord` module from `fp-ts` exposes some preconstructed instances
|
||||
// of `Ord<T>` for a few primitive `T`s such as `ordString: Ord<string>` or
|
||||
// `ordNumber: Ord<number>`.
|
||||
// HINT: The primitive type modules from `fp-ts` (`number`, `string`...)
|
||||
// expose some preconstructed instances of `Ord<T>` for said primitives such as
|
||||
// `string.Ord: Ord<string>` or `number.Ord: Ord<number>`.
|
||||
|
||||
export const sortStrings: (
|
||||
strings: ReadonlyArray<string>,
|
||||
|
@ -48,12 +47,13 @@ export const sortNumbers: (
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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
|
||||
// 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: (
|
||||
numbers: ReadonlyArray<number>,
|
||||
|
@ -64,18 +64,18 @@ export const sortNumbersDescending: (
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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>>`.
|
||||
//
|
||||
// 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
|
||||
// to take a look at `Option.getOrd`.
|
||||
// to take a look at `option.getOrd`.
|
||||
|
||||
export const sortOptionalNumbers: (
|
||||
optionalNumbers: ReadonlyArray<Option.Option<number>>,
|
||||
) => ReadonlyArray<Option.Option<number>> = unimplemented;
|
||||
optionalNumbers: ReadonlyArray<Option<number>>,
|
||||
) => ReadonlyArray<Option<number>> = unimplemented;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SORT COMPLEX OBJECTS //
|
||||
|
@ -92,11 +92,11 @@ export const sortOptionalNumbers: (
|
|||
//
|
||||
// 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
|
||||
// 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 {
|
||||
readonly name: string;
|
||||
readonly age: Option.Option<number>;
|
||||
readonly age: Option<number>;
|
||||
}
|
||||
|
||||
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,
|
||||
// we want to sort them by name.
|
||||
//
|
||||
// HINT: Take a look at `ReadonlyArray.sortBy`
|
||||
// HINT: Take a look at `readonlyArray.sortBy`
|
||||
|
||||
export const sortPersonsByAgeThenByName: (
|
||||
person: ReadonlyArray<Person>,
|
||||
|
|
Reference in a new issue