diff --git a/exercises/reader.test.ts b/exercises/reader.test.ts new file mode 100644 index 0000000..9d6d58c --- /dev/null +++ b/exercises/reader.test.ts @@ -0,0 +1,70 @@ +import * as Read from './reader' +import * as R from 'fp-ts/Reader' +import * as M from 'fp-ts/Monoid' + +describe('reader', () => { + it('one', () => { + expect(Read.one([R.of(1), R.of(2), R.of(3)])(true)).toEqual(6) + expect(Read.one([])(true)).toEqual(0) + }) + it('two', () => { + expect( + Read.two([R.of(1), R.of(2), R.of(3)], M.monoidSum)(true) + ).toEqual(6) + expect(Read.two([], M.monoidSum)(true)).toEqual(0) + + const reader2 = Read.two( + [R.of(1), R.of(2), R.of(3), (a: number) => a + 1], + M.monoidProduct + ) + expect(reader2(10)).toEqual(66) + expect(reader2(-1)).toEqual(0) + }) + it('three', () => { + const reader = Read.three( + a => b => `${a} + ${b}`, + (rA: number) => rA * 2, + (rB: number) => rB * 3 + ) + expect(reader(0)).toEqual('0 + 0') + expect(reader(2)).toEqual('4 + 6') + }) + it('four', () => { + const translationsEnglish = { + greeting: 'Good morning', + happyBirthday: 'Happy birthday', + genericPleasantry: 'Have a nice day', + } + const reader = Read.four('Mr Horse', new Date('2020-01-01')) + expect( + reader({ + translations: translationsEnglish, + date: new Date('2020-01-01'), + }) + ).toEqual('Good morning, Mr Horse! Happy birthday!') + expect( + reader({ + translations: translationsEnglish, + date: new Date('2021-02-01'), + }) + ).toEqual('Good morning, Mr Horse! Have a nice day.') + + const translationsItalian = { + greeting: 'Buongiorno', + happyBirthday: 'Buon compleanno', + genericPleasantry: 'Buona giornata', + } + expect( + reader({ + translations: translationsItalian, + date: new Date('2020-01-01'), + }) + ).toEqual('Buongiorno, Mr Horse! Buon compleanno!') + expect( + reader({ + translations: translationsItalian, + date: new Date('2021-02-01'), + }) + ).toEqual('Buongiorno, Mr Horse! Buona giornata.') + }) +}) diff --git a/exercises/reader.ts b/exercises/reader.ts new file mode 100644 index 0000000..7e46eeb --- /dev/null +++ b/exercises/reader.ts @@ -0,0 +1,54 @@ +import * as R from 'fp-ts/Reader' +import * as M from 'fp-ts/Monoid' +import { pipe } from 'fp-ts/function' +import * as A from 'fp-ts/Apply' + +// given an array of Reader values, return one return value with all the values +// inside summed +export const one = ( + as: R.Reader[] +): R.Reader => pipe(as, M.fold(R.getMonoid(M.monoidSum))) + +// given an array of Reader values, and a Monoid that knows how to combine the +// values, return a single Reader value with all the contents combined +export const two = ( + as: R.Reader[], + monoid: M.Monoid +): R.Reader => pipe(as, M.fold(R.getMonoid(monoid))) + +// given a function shaped `A` -> `B` -> `C`, a Reader and a Reader, +// return a Reader +export const three = ( + f: (a: A) => (b: B) => C, + readerA: R.Reader, + readerB: R.Reader +): R.Reader => pipe(readerA, R.map(f), R.ap(readerB)) + +type Env = { + translations: { + greeting: string + happyBirthday: string + genericPleasantry: string + } + date: Date +} + +const thingOne = (name: string) => ({ translations }: Env) => + `${translations.greeting}, ${name}!` + +const thingTwo = (birthday: Date) => ({ date, translations }: Env) => + date.getMonth() == birthday.getMonth() && + date.getDate() == birthday.getDate() + ? `${translations.happyBirthday}!` + : `${translations.genericPleasantry}.` + +// given the functions `thingOne` and `thingTwo`, combine them to make a +// function that creates a birthday greeting. +export const four = ( + name: string, + birthday: Date +): R.Reader => + pipe( + A.sequenceT(R.reader)(thingOne(name), thingTwo(birthday)), + R.map(([a, b]) => `${a} ${b}`) + )