繁体   English   中英

处理对象时如何组合/管道函数?

[英]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'
  }
}

现在,我有两个函数: validateUservalidatePost 它们都返回一个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其组成validateUservalidatePost 但是,我如何只将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)

相关阅读

是的,您应该创建validateRequestsavePostToDb方法,它们会给您一个布尔值。 然后,您可以使用这两种新方法简单地创建两个 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM