Skip to content

Instantly share code, notes, and snippets.

@kirillrogovoy
Created February 3, 2023 07:18
Show Gist options
  • Save kirillrogovoy/c9bb5c8f6fd277b9edf42a4e1eb0f8f8 to your computer and use it in GitHub Desktop.
Save kirillrogovoy/c9bb5c8f6fd277b9edf42a4e1eb0f8f8 to your computer and use it in GitHub Desktop.
let debugCounter = 0
export function mmap<
RI,
A,
AE = A extends AR.AsyncResult<any, infer E>
? E
: A extends Promise<any>
? Error
: A extends R.Error<infer E>
? E
: never,
>(
fn: (
argInner: A extends AR.AsyncResult<infer U, any>
? U
: A extends Promise<infer U>
? U
: A extends R.Result<infer U, never>
? U
: A extends R.Result<never, any>
? never
: never,
) => RI,
): (arg: A) => RI extends AR.AsyncResult<infer U, infer E>
? AR.AsyncResult<U, E | AE>
: RI extends Promise<infer U>
? A extends AR.AsyncResult<never, infer E>
? AR.AsyncResult<U, E | AE>
: AR.AsyncResult<U, Error | AE>
: // RI is Result
RI extends R.Result<infer U, never>
? A extends Promise<any>
? AR.AsyncResult<U, AE>
: RI
: RI extends R.Result<never, infer U>
? A extends Promise<any>
? AR.AsyncResult<never, U | AE>
: R.Error<U | AE>
: // RI is scalar
A extends AR.AsyncResult<any, infer E>
? AR.AsyncResult<RI, E | AE>
: A extends Promise<any>
? AR.AsyncResult<RI, Error | AE>
: A extends R.Result<any, infer E>
? R.Result<RI, E | AE>
: RI {
const debug = false
const id = debugCounter++
const log = debug
? (...msg: any) => console.log(id, ...msg)
: () => {
// noop
}
return ((arg: any) => {
let ir: { r: RI } | undefined
if (!isPromise(arg)) {
if (!isResult(arg)) {
ir = { r: fn(arg) }
if (!isPromise(ir.r)) {
log('sync -> sync')
return ir.r
}
}
if (isResult(arg) && R.isError(arg)) {
log('skip error')
return arg
}
if (isResult(arg) && R.isOk(arg)) {
ir = { r: fn(R.getExn(arg) as any) }
if (!isPromise(ir.r)) {
log('sync -> sync result')
return ir.r
}
}
}
// R will be a promise
log('async')
return (async () => {
log('arg', arg)
const skip = Symbol('skip')
const innerArg = await (async () => {
const arg$ = await arg
log('arg$', arg$)
if (!isResult(arg$)) {
log('is not result')
return arg$
}
log('is result')
if (R.isError(arg$)) {
return skip
}
return R.getExn(arg$) as any
})()
log('innerArg', innerArg)
if (innerArg === skip) {
log('skip')
return arg
}
const innerResult = await (ir ? ir.r : fn(innerArg))
log('innerResult', innerResult)
if (!isResult(innerResult)) {
return R.Ok(innerResult)
}
return innerResult
})().catch((e) => R.Error(e)) as any
}) as any
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment