Created
November 3, 2018 16:41
-
-
Save YBogomolov/d38d8b1415329a6f2b0306511361b31e to your computer and use it in GitHub Desktop.
Free monads example with fp-ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { foldFree, liftF } from 'fp-ts/lib/Free'; | |
import { identity, Identity } from 'fp-ts/lib/Identity'; | |
// Task: get all users named 'John' and make them admins | |
interface User { | |
id: number; | |
name: string; | |
age: number; | |
isAdmin: boolean; | |
} | |
const fakeUsers: User[] = [ | |
{ | |
id: 1, | |
name: 'John Connor', | |
age: 32, | |
isAdmin: false, | |
}, | |
{ | |
id: 2, | |
name: 'John Smith', | |
age: 18, | |
isAdmin: false, | |
}, | |
{ | |
id: 3, | |
name: 'Mark Brian', | |
age: 28, | |
isAdmin: false, | |
}, | |
]; | |
declare module 'fp-ts/lib/HKT' { | |
interface URI2HKT<A> { | |
EitherResult: ResultF<A>; | |
} | |
} | |
const OpsURI = 'EitherResult'; | |
type OpsURI = typeof OpsURI; | |
/* | |
https://github.com/gcanti/fp-ts/issues/528#issuecomment-405913924 | |
Roughly speaking: | |
an operation which normally would return void is modelled with `more: A` | |
an operation which normally would return a type T is modelled with `more: (t: T) => A` | |
*/ | |
class Search<A> { | |
readonly _tag: 'Search' = 'Search'; | |
readonly _A!: A; | |
readonly _URI!: OpsURI; | |
constructor( | |
readonly params: Partial<User>, | |
readonly more: (users: User[]) => A, | |
) {} | |
} | |
class MakeAdmin<A> { | |
readonly _tag: 'MakeAdmin' = 'MakeAdmin'; | |
readonly _A!: A; | |
readonly _URI!: OpsURI; | |
constructor( | |
readonly users: User[], | |
readonly more: (users: User[]) => A, | |
) {} | |
} | |
class Save<A> { | |
readonly _tag: 'Save' = 'Save'; | |
readonly _A!: A; | |
readonly _URI!: OpsURI; | |
constructor( | |
readonly users: User[], | |
readonly more: (res: boolean[]) => A, | |
) {} | |
} | |
export type ResultF<A> = Search<A> | MakeAdmin<A> | Save<A>; | |
const search = (params: Partial<User>) => liftF(new Search(params, (a) => a)); | |
const makeAdmins = (users: User[]) => liftF(new MakeAdmin(users, (a) => a)); | |
const save = (users: User[]) => liftF(new Save(users, (a) => a)); | |
const program = search({ name: 'John' }) | |
.chain((johns) => makeAdmins(johns)) | |
.chain((admins) => save(admins)); | |
const exaustive = (x: never): never => x; | |
export function identityInterpreter<A>(fa: ResultF<A>): Identity<A> { | |
switch (fa._tag) { | |
case 'Search': | |
return identity.of(fa.more(fakeUsers.filter((u) => u.name.includes(fa.params.name)))); | |
case 'MakeAdmin': | |
return identity.of(fa.more(fa.users.map((u) => ({ ...u, isAdmin: true})))); | |
case 'Save': | |
return identity.of(fa.more(fa.users.map((u) => u.isAdmin))); | |
} | |
return exaustive(fa); | |
} | |
const result = foldFree(identity)(identityInterpreter, program); | |
console.log(result.value); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment