处理对象时如何撰写/传递函数?

时间:2020-11-01 23:13:28

标签: javascript functional-programming

说我有一个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
}

我是否应该创建由validateRequestvalidateUser组成的函数validatePost?但是,那我怎么只给post对象呢?如果savePostToDb也需要用户ID怎么办?

savePostToDb

希望这些问题是有道理的。我是FP的新手,虽然我了解FP的范例,但我无法设计一个简单的程序

谢谢!

2 个答案:

答案 0 :(得分:1)

道具

我们编写了一个简单的 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()
}

也许

最后我们为 NothingJust 提供实现 -

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)

相关阅读

答案 1 :(得分:0)

是的,你应该创建 <img src='www.awesomesite.com/awesomeImage.svg' ref={el => this.imageRef = el} onLoad={() => console.log(this.imageRef.src) } /> 和一个 validateRequest 方法,它们会给你一个布尔值。然后,您可以使用这两种新方法简单地创建两个 Maybe-Functions 并将其组合在一起。看看我的例子,我会如何简单地做到这一点:

savePostToDb