[英]How to compose/pipe functions when dealing with objects?
假设我有一个Request
对象:
{
user: { /* user data: username, email, etc. */ }
post: { /* post data: content, date, etc. */ }
}
Request
对象示例:
{
user: {
id: '123'
username: 'kibe'
email: 'blabla@gmail.com'
}
post: {
content: 'my new post!'
date: '20/02/2004'
}
}
现在,我有两个函数: validateUser
和validatePost
。 它们都返回一个Maybe
monad,因为它们可能会失败。
我怎么能做这样的事情?
function savePost (request) {
return request
|> validateUser // if validateUser returns either Success(user) or Failure(error), how would I pass down the post?
|> validatePost
|> savePostToDb
|> ok
}
我应该创建一个函数validateRequest
其组成validateUser
和validatePost
? 但是,我如何只将post
对象提供给savePostToDb
? 如果savePostToDb
也需要用户 ID 怎么办?
function savePost (request) {
return request
|> validateRequest // returns an Either monad
|> savePostToDb // only requires user ID and the post, how would I pass these down?
|> ok
}
希望这些问题是有道理的。 我是 FP 的新手,虽然我了解它的范式,但我无法设计一个简单的程序
谢谢!
支柱
我们编写了一个简单的prop
函数,它可以安全地查找对象的属性。 如果它们存在,我们会得到一个Just
-wrapped 值,否则我们Nothing
-
const fromNullable = x =>
x == null
? Nothing
: Just(x)
const prop = k => t =>
fromNullable(t[k])
道具
虽然你有嵌套的属性,所以让我们这样做,这样我们就可以使用任何props
序列任意深入地挖掘你的对象 -
const props = (k, ...ks) => t =>
ks.length
? prop(k)(t).bind(props(...ks))
: prop(k)(t)
证实
现在我们可以编写一个简单的validate
协程 -
function* validate (t)
{ const id = yield props("user", "id")(t)
const username = yield props("user", "username")(t)
const email = yield props("user", "email")(t)
const post = yield props("post", "content")(t)
const date = yield props("post", "date")(t)
return Just({ id, username, email, post, date }) // <- Just(whateverYouWant)
}
const doc =
{ user:
{ id: '123'
, username: 'kibe'
, email: 'blabla@gmail.com'
}
, post:
{ content: 'my new post!'
, date: '20/02/2004'
}
}
coroutine(validate(doc)).bind(console.log)
使用有效的doc
我们可以看到 -
{
id: '123',
username: 'kibe',
email: 'blabla@gmail.com',
post: 'my new post!',
date: '20/02/2004'
}
使用无效文档, otherdoc
-
const otherdoc =
{ user:
{ id: '123'
, username: 'kibe'
, // <- missing email
}
, post:
{ content: 'my new post!'
, // <- missing date
}
}
coroutine(validate(otherdoc)).bind(console.log)
// no effect because validate returns a Nothing!
协程
coroutine
实现很简单,最重要的是它对任何 monad 都是通用的 -
function coroutine (f)
{ function next (v)
{ let {done, value} = f.next(v)
return done ? value : value.bind(next)
}
return next()
}
也许
最后,我们为Nothing
and Just
提供实现 -
const Nothing =
({ bind: _ => Nothing })
const Just = v =>
({ bind: f => f(v) })
演示
展开下面的代码段以在您自己的浏览器中验证结果 -
const Nothing = ({ bind: _ => Nothing }) const Just = v => ({ bind: f => f(v) }) const fromNullable = x => x == null ? Nothing : Just(x) const prop = k => t => fromNullable(t[k]) const props = (k, ...ks) => t => ks.length ? prop(k)(t).bind(props(...ks)) : prop(k)(t) function coroutine (f) { function next (v) { let {done, value} = f.next(v) return done ? value : value.bind(next) } return next() } function* validate (t) { const id = yield props("user", "id")(t) const username = yield props("user", "username")(t) const email = yield props("user", "email")(t) const post = yield props("post", "content")(t) const date = yield props("post", "date")(t) return Just({ id, username, email, post, date }) } const doc = {user:{id:'123',username:'kibe',email:'blabla@gmail.com'},post:{content:'my new post!',date:'20/02/2004'}} coroutine(validate(doc)).bind(console.log)
相关阅读
是的,您应该创建validateRequest
和savePostToDb
方法,它们会给您一个布尔值。 然后,您可以使用这两种新方法简单地创建两个 Maybe-Functions 并将其组合在一起。 看看我的例子,我会如何简单地做到这一点:
// Here some simple Left/Right to Just and Nothing (Maybe)-Monade function to work with
const Left = x => f => _ => f(x);
const Right = x => _ => g => g(x);
const Nothing = Left();
const Just = Right;
// your maybe-validateRequest function
const maybeValidateRequest = request =>
validateRequest(request)
? Just(user)
: Nothing
// your maybe-postToDb function
const maybePostToDb = request =>
savePostToDb(request)
? Just(request)
: Nothing
// and finally compose those two maybes and do the postRequest finally
const savePost = request =>
maybeValidateRequest(request)
(() => console.error("Validation failed"))
(_ =>
maybePostToDb(request)
(() => console.error("Save to DB failed")))
(_ => postRequest(request)) // when it's everything passed,the postRequest can be safely made
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.