gpt4 book ai didi

node.js - 有没有一种方法可以编写一个内部带有 promise 的Maybe.map?

转载 作者:太空宇宙 更新时间:2023-11-03 22:09:25 25 4
gpt4 key购买 nike

我正在关注这篇出色的文章的前半部分,但是有一个地方让我陷入困境。 https://jrsinclair.com/articles/2016/marvellously-mysterious-javascript-maybe-monad/

我实现了一个非常相似的Maybe monad,但是我需要传递给map的函数之一是异步的。理想情况下,我可以将.then()和map()结合使用。我想做这样的事情...

const getToken = async (p) => {
let result = utils.Maybe.of(await makeAICCCall(p.aiccsid, p.aiccurl))
.map(parseAuthenticatedUser)
.thenMap(syncUserWithCore) <-- I can't figure this out
.map(managejwt.maketoken)
.value

return result;
}


我已经尝试了所有我能想到的,但是我仍然无法弄清楚。

最佳答案

自然转变

数据容器的嵌套可能会变得凌乱,但是有一种众所周知的技术可以使它们保持扁平-下面的eitherToPromise被认为是natural transformation –它将Either转换为Promise,从而可以在.then链中进行扁平化,同时还可以防止您的价值/错误线交叉

注意:您可能想使用makeAICCall而不是Either返回LeftRightMaybe),因为您将能够返回错误消息(而不是Nothing信息较少)

import { Left, Right, eitherToPromise } from './Either'

const makeAICCall = (...) =>
someCondition
? Left (Error ('error happened'))
: Right (someResult)

const getToken = p =>
makeAICCall (p.aiccsic, p.aiccurl) // => Promise<Either<x>>
.then (eitherToPromise) // => Promise<Promise<x>>
// => auto-flattened to Promise<x>
.then (syncUserWithCore) // => Promise<x>
.then (managejwt.maketoken) // => Promise<x>


提供您最喜欢的 Either实现

// Either.js

export const Left = x =>
({
fold: (f,_) => f (x),
// map: f => Left (x),
// chain: ...,
// ...
})

export const Right = x =>
({
fold: (_,f) => f (x),
// map: f => Right (f (x)),
// chain: ...,
// ...
})

export const eitherToPromise = m =>
m.fold (x => Promise.reject (x), x => Promise.resolve (x))


可运行的演示



const someAsyncCall = x =>
new Promise (r => setTimeout (r, 1000, x))

const authenticate = ({user, password}) =>
password !== 'password1'
? Left (Error ('invalid password'))
: Right ({user, id: 123})

const someSyncCall = token =>
Object.assign (token, { now: Date.now () })

const getToken = x =>
someAsyncCall (x)
.then (authenticate)
.then (eitherToPromise)
.then (someSyncCall)

// minimal dependencies
const Left = x =>
({ fold: (f,_) => f (x) })

const Right = x =>
({ fold: (_,f) => f (x) })

const eitherToPromise = m =>
m.fold (x => Promise.reject (x), x => Promise.resolve (x))

// test it
getToken ({user: 'alice', password: 'password1'})
.then (console.log, console.error)
// 1 second later ...
// { user: 'alice', id: 123, now: 1509034652179 }

getToken ({user: 'bob', password: 'password2'})
.then (console.log, console.error)
// 1 second later ...
// Error: invalid password ...





嘿,看那个

我们上面的解决方案产生了一系列 .then调用– an answer to your previous question演示了如何以不同的方式表示这样的程序

可为空

您应该尽力编写具有明确定义的 domaincodomain的函数–例如,您应该可以说,


  我的函数接受一个字符串(域)并返回一个数字(共域)–匿名wiseman


并避免编写描述如下的函数:


  它可以采用数字或字符串,并返回数组,但也可以返回未定义。哦,有时会抛出错误。就是这样,我很确定。 –匿名无知


但是当然,有时我们会处理 nullundefined。我们如何以“功能性方式”处理它-这就是您想知道的,对吗?

如果您发现自己遇到了函数的可为空的共域(即可以返回可为空的共域),则可以创建一个小助手来将其强制为所需的类型。我们将再次与Either进行演示,只是稍后将其与原始代码绑定



const Left = x =>
({ fold: (f,_) => f (x) })

const Right = x =>
({ fold: (_,f) => f (x) })

const eitherFromNullable = (x, otherwise = x) =>
x === null ||
x === undefined
? Left (otherwise)
: Right (x)

// !! nullable codomain !!
const find = (f, xs) =>
xs.find (x => f (x))

// example data
const data =
[1,2,3,4,5]

// perform safe lookups by wrapping unsafe find in eitherFromNullable
eitherFromNullable (find (x => x > 3, data))
.fold (console.error, console.log)
// <console.log> 4

eitherFromNullable (find (x => x > 5, data))
.fold (console.error, console.log)
// <console.error> undefined

eitherFromNullable (find (x => x > 5, data), Error (`couldn't find a big number !`))
.fold (console.error, console.log)
// <console.error> Error: couldn't find a big number !





空值和自然转换

请记住,我们尽最大努力避免可为空,但有时我们无能为力。为了说明这与原始代码的关系,让我们假设 makeAICCall而不是返回Either,而是返回x或null

我们只用 eitherFromNullable对其进行筛选-新代码以粗体显示

const getToken = p =>
makeAICCall (p.aiccsic, p.aiccurl) // => Promise<x?> could be null !!
.then (x => // => Promise<Either<x>>
eitherFromNullable (x, Error ('bad aic call')))

.then (eitherToPromise) // => Promise<Promise<x>>
// => auto-flattened to Promise<x>
.then (syncUserWithCore) // => Promise<x>
.then (managejwt.maketoken) // => Promise<x>


停止讨厌lambdas

您可能想抛弃那个lambda,对吗?好的,只是不要将它变成恋物癖。

const eitherFromNullable = otherwise => x =>
x == null ? Left (otherwise) : Right (x)

// ooooh, yeah, you like that
makeAICCall (p.aiccsic, p.aiccurl)
.then (eitherFromNullable (Error ('bad aic call')))
.then (eitherToPromise)
.then ...


不要卡住

Nullable并不意味着任何东西–您可以在程序上下文中决定它的含义。

const eitherFromNullable = (x, otherwise = x) =>
// we consider null and undefined nullable,
// everything else is non-null
x === null ||
x === undefined
? Left (otherwise)
: Right (x)


您可以确定 false0以及空字符串 ''也是“可空的” –或者,您也可以轻松地确定具有非常特定的适配器 eitherFromNulleitherFromUndefinedeitherFromBoolean等–这是你的程序;由你决定!

我觉得我开始重复自己了^ _ ^'

使其常规

因此,您要说的是您的程序中有很多区域都是不可避免的空值;也许这是您无法摆脱的依赖。我们将通过以下内容想象您的代码库

// no one wants to do this for every endpoint!
const getUser = id =>
new Promise ((resolve, reject) =>
request ({url: '/users', id}, (err, res) =>
err
? reject (err)
: res.status === 403
? reject (Error ('unauthorized'))
res.body == null ?
? reject (Error ('not found'))
: resolve (User (JSON.parse (res.body)))))



它使用的是 request,它具有较旧的Node样式的回调接口,我们已经厌倦了将它们包装在Promise中
如果请求者未经授权查看请求的资源,API端点将以 403状态响应
我们与之交谈的API端点有时会因资源缺失而以状态为200(而不是404)的 null进行响应;例如 /users/999,其中 999是未知的用户ID,不会触发错误,但会获取一个空的响应正文
API将针对所有其他请求以有效的JSON文档进行响应


我们希望可以使用 request以外的其他方式,但是主管说不。我们希望API端点具有不同的行为,但这是我们无法控制的。尽管如此,编写一个好的程序仍在我们的能力之内

// functional programming is about functions
const safeRequest = (type, ...args) =>
new Promise ((resolve, reject) =>
request[type] (args, (err, res) =>
err
? reject (err)
: res.status === 403
? reject (Error ('unauthorized'))
res.body == null ?
? reject (Error ('not found'))
: resolve (JSON.parse (res.body))))

const getUser = id =>
safeRequest ('get', {url: '/users', id})

const createUser = fields =>
safeRequest ('post', {url: '/users', fields})

const updateUser = (id, fields) =>
safeRequest ('put', {url: '/users', id, fields})


可以进一步改善吗?当然可以,但是就算你走了,也没有错。所有必需的检查都针对每个端点进行,因为它们是使用 safeRequest定义的

好的,所以您想进一步吗?没问题。这是您的程序,随心所欲!

const promisify = f => (...args) =>
new Promise ((resolve, reject) =>
f (...args, (err, x) =>
err ? reject (err) : resolve (x)))

const promiseFromResponseStatus = res =>
res.status === 403 // or handle other status codes here too !
? Promise.reject (Error ('unauthorized'))
: Promise.resolve (res)

const promiseFromNullableResponse = res =>
res.body == null // or res.body == '', etc
? Promise.reject (Error ('not found'))
: Promise.resolve (res.body)

const safeRequest = (type, ...args) =>
promisify (request [type]) (...args)
.then (promiseFromResponseStatus)
.then (promiseFromNullableResponse)
.then (JSON.parse)

const getUser = id =>
safeRequest ('get', {url: '/users', id})

const createUser ...

....

关于node.js - 有没有一种方法可以编写一个内部带有 promise 的Maybe.map?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46958117/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com