This repository has been archived on 2023-05-29. You can view files and clone it, but cannot push or open issues or pull requests.
fp-ts-training/src/exo4/exo4.ts

108 lines
3.8 KiB
TypeScript

// `fp-ts` training Exercise 4
// Dependency injection with `Reader`
import { Reader } from "fp-ts/Reader";
import { reader } from "fp-ts";
import { absurd, flow, pipe } from "fp-ts/lib/function";
// Sometimes, a function can have a huge amount of dependencies (services,
// repositories, ...) and it is often impractical (not to say truly annoying)
// to thread those values through multiple levels of the call stack.
//
// That's precisely what dependency injection is meant to solve, and it's not a
// concept exclusive to OOP!
// In the world of FP, the so-called `Reader` construct (just a fancy word for
// a partially applied function) offers precisely those capabilities.
///////////////////////////////////////////////////////////////////////////////
// SETUP //
///////////////////////////////////////////////////////////////////////////////
// Let's consider a small range of countries (here, France, Spain and the USA):
export enum Country {
France = "France",
Spain = "Spain",
USA = "USA",
}
///////////////////////////////////////////////////////////////////////////////
// ASK //
///////////////////////////////////////////////////////////////////////////////
// These countries have different rules on how to add exclamation to a sentence:
// - french speakers will add an exclamation point at the end preceded by a
// space: "Youpi !"
// - spanish speakers begin the sentence with an inverted exclamation point and
// finish it by a regular one: `¡Olé!`
// - english speakers will have no space before the exclamation point at the end
// of the sentence: `Yeah!`
//
// The following function should take a sentence as input and return a `Reader`
// that will at some point expect a `Country` and return the sentence with the
// proper exclamation style.
//
// HINT: Take a look at `reader.ask` to access the environment value
export const exclamation: (sentence: string) => Reader<Country, string> = (
sentence,
) =>
pipe(
reader.ask<Country>(),
reader.map((c) =>
c === Country.USA
? `${sentence}!`
: c === Country.France
? `${sentence} !`
: c === Country.Spain
? `¡${sentence}!`
: absurd(c)
),
);
// Obviously, different countries often mean different languages and so
// different words for saying "Hello":
export const sayHello = (country: Country): string => {
switch (country) {
case Country.France:
return "Bonjour";
case Country.Spain:
return "Buenos dìas";
case Country.USA:
return "Hello";
default:
return absurd(country);
}
};
// Using the `sayHello` function above, write the `greet` function that
// delivers a personalized greeting to a person based on their name.
// The output should look something like `${hello}, ${name}`.
//
// HINT: Remember that a `Reader<R, T>` is just an alias for `(r: R) => T`
//
// HINT: You can look into `reader.map` to modify the output of a `Reader`
// action.
export const greet: (name: string) => Reader<Country, string> = (name) =>
pipe(reader.ask<Country>(), reader.map((c) => `${sayHello(c)}, ${name}`));
// Finally, we are going to compose multiple `Reader`s together.
//
// Sometimes, a simple greeting is not enough, we want to communicate our
// excitement to see the person.
// Luckily, we already know how to greet them normally (`greet`) and how to
// add excitement to a sentence (`exclamation`).
//
// Compose those two to complete the `excitedlyGreet` function below:
//
// HINT: As with other wrapper types in `fp-ts`, `reader` offers a way of
// composing effects with `reader.chain`.
export const excitedlyGreet: (name: string) => Reader<Country, string> = flow(
greet,
reader.flatMap(exclamation),
);