Created
June 29, 2019 09:40
-
-
Save acdcjunior/5d8364a93fd8db8ad654d502e9e0e6dc to your computer and use it in GitHub Desktop.
Dependency Injection via Monads in JavaScript/TypeScript
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 axios, {AxiosInstance} from 'axios'; | |
interface Context { | |
get(s: string): any; | |
headersForResource(id: number): any; | |
} | |
class Contextual<T> { | |
constructor(private g: (ctx: Context) => T) {} | |
run(ctx: Context): T { return this.g(ctx) }; | |
map<B>(aToB: (A) => B): Contextual<B> { return new Contextual<B>((ctx: Context) => aToB(this.g(ctx))); } | |
flatMap<B>(f: (A) => Contextual<B>): Contextual<B> { return new Contextual<B>((ctx: Context) => f(this.g(ctx)).run(ctx)); } | |
} | |
function combine<RESULT>(ctxls: Contextual<any>[], combinator: (results: any[]) => RESULT): Contextual<RESULT> { | |
return new Contextual<RESULT>(ctx => { | |
return combinator(ctxls.map(c => c.run(ctx))); | |
}); | |
} | |
///////////////////////////////////////////// USER | |
interface User {} | |
let userApi = new Contextual<AxiosInstance>((ctx: Context) => { | |
return axios.create({baseURL: ctx.get('USERS_URL'), headers: {...ctx.headersForResource(123)}}); | |
}); | |
const userByIdQuery = (id) => (userApi: AxiosInstance) => userApi.get('/users/' + id).then(r => r.data); | |
const userById = (id: number): Contextual<Promise<User>> => userApi.map(userByIdQuery(id)); | |
///////////////////////////////////////////// POST | |
interface Post {} | |
let postApi = new Contextual<AxiosInstance>((ctx: Context) => { | |
return axios.create({baseURL: ctx.get('POSTS_URL'), headers: {...ctx.headersForResource(123)}}); | |
}); | |
// written as one function only | |
let postById = (id): Contextual<Promise<Post>> => postApi.map((postApi: AxiosInstance) => { | |
return postApi.get('/posts/' + id).then(r => r.data); | |
}); | |
///////////////////////////////////////////// COMBINING RESULTS: USER AND POST | |
let userOneName = userById(1).map(userPromise => userPromise.then(u => u.name)); | |
let postOneTitle: Contextual<Promise<Post>> = postById(1).map(async pP => (await pP).title); | |
let userAndPostCombinedManually = (userPromise) => new Contextual<Promise<any>>(async t => { | |
let us = await userPromise; | |
let pp = await postOneTitle.run(t); | |
return {us, pp}; | |
}); | |
let userAndPostCombinedViaHelper = combine([userById(1), postById(1)], async ([userPromise, postPromise]) => { | |
const u = await userPromise; | |
const p = await postPromise; | |
return {...u, ...p} | |
}); | |
/////////////////////////// EXECUTION, WHEN YOU ACTUALLY PROVIDE THE CTX | |
(async () => { | |
const ctx = { | |
get() { return 'http://jsonplaceholder.typicode.com/'}, | |
headersForResource() { return {Authorization: `Bearer JWT`, 'X-UFP': 'UFP'}; } | |
}; | |
const actualUserOne = await userOneName.run(ctx); | |
console.log(actualUserOne); | |
console.log('--------------------'); | |
const flattened = await userById(1).flatMap(userAndPostCombinedManually).run(ctx); | |
console.log(flattened); | |
console.log('--------------------'); | |
console.log(await userAndPostCombinedViaHelper.run(ctx)); | |
})(); |
Author
acdcjunior
commented
Oct 7, 2020
function combine<E, R, A, B>(a: Reader<E, A>, b: Reader<E, B>, g: (a: A, b: B) => R): Reader<E, R> {
return Reader<E, R>((ctx: E) => {
return g(a.run(ctx), b.run(ctx));
});
}
const reader: Reader<TokenJwt| (() => TokenJwt), string> = combine(sa2Api2, sa2Api2, (sa2Api2a1, sa2Api2a2) => {
return 'asas'
});
interface AA { aa: string; }
interface BB { bb: string; }
interface CC { cc: string; }
const r: Reader<AA, BB> = Reader((a: AA) => ({ bb: 'bb' }) as BB);
const r2: Reader<AA, CC> = Reader((a: AA) => ({ cc: 'cc' }) as CC);
function ReaderComb<E, R1, R2>(r1: Reader<E, R1>, r2: Reader<E, R2>) {
return Reader((t: E) => [r1.run(t), r2.run(t)] as [R1, R2])
}
const bc = (b: BB, c: CC) => console.log(b.bb, c.cc)
r.map(b => r2.map(c => {
bc(b, c)
}));
const reader1x: Reader<AA, string> = ReaderComb(r, r2).map(([bb, cc]) => {
return bb.bb + cc.cc;
});
const reader1: Reader<AA, string> = Reader((t: AA) => [r.run(t), r2.run(t)]).map(([bb, cc]) => {
return bb.bb + cc.cc;
});
Reader((t: TokenJwt2019Usuario) => [sa2Api2.run(t), sa2Api2.run(t)]).map()
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment