在函数式编程中,是否有一种干净的方法可以对某些数据执行许多操作,而无需将数据显式传递到每个函数中?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在函数式编程中,是否有一种干净的方法可以对某些数据执行许多操作,而无需将数据显式传递到每个函数中?相关的知识,希望对你有一定的参考价值。

假设我有一些函数可以对某些数据执行业务逻辑:

function addEmployees(data, numberOfNewEmployees){
    // Business logic...
    data.employeeCount += numberOfNewEmployees;
    return data;
}

function withdrawFunds(data, withdrawAmount){
    // Business logic...
    data.checkingAccount -= withdrawAmount;
    return data;
}

function completeAnOrder(data){
    // Business logic...
    data.pendingOrders -- 1;
    return data;
}

现在,要对某些数据执行几个操作,我有类似的东西(让我们假设数据是通过复制传递的):

const data = {
    employeeCount: 5,
    checkingAccount: 5000,
    pendingOrders: 2
}

let newData = addEmployees(data, 2);
newData = withdrawFunds(newData, 2000);
newData = completeAnOrder(newData);

我很好奇是否在函数式编程世界中有一种优雅的方法可以实现更接近这一点的方法:

const data = {
    employeeCount: 5,
    checkingAccount: 5000,
    pendingOrders: 2
}

let biz = createBiz(data);

const newData = biz.addEmployees(2)
    .withdrawFunds(2000)
    .completeAnOrder()
    .toValue();

javascript中我知道一个对象可以返回this,这就是JQuery方法链接的工作原理。

但在功能世界中有一种优雅的方法可以做类似的事情吗?我意识到我可能会试图将OOP的想法强加到FP中。

Monad是否解决了这个问题?为特定的业务逻辑创建自己的自定义Monads是否有意义?

答案

这在很大程度上取决于语言和语言提供的工具。

Clojure,这是homoiconic,这样的任务通常使用宏来解决。在这种情况下,这将使用“线程”宏来完成。

说我有你的功能:

;  All of these functions return the modified data
(defn add-employees [data number-of-new-employees]
  ...)

(defn withdraw-funds [data withdraw-amount]
  ...)

(defn complete-an-order [data]
  ...)

由于“this”(data)是第一个参数,我可以使用->自动“线程化”每个调用的参数:

(def data {:employee-count 5,
           :checking-account 5000,
           :pending-orders 2})

(-> data
  (add-employees 2) ; The result of this gets passed as the first argument to withdraw-funds
  (withdraw-funds 2000) ; Then the result of this gets passed to complete-an-order...
  (complete-an-order) ; Same as above
  (to-value))

在宏扩展之后,这基本上变成了:

(to-value (complete-an-order (withdraw-funds (add-employees data 2) 2000)))

但是使用->将来更容易阅读和更容易。

另一答案

你会使用作文。在Haskell中,如果操作是在结构上运行并返回新结构的纯函数,而不是I / O操作,那么您可以用几种不同的方式编写,例如:toValue . completeOrder . withdrawFunds 2000 . addEmployees 2 $ data。 (你也可以用&从左到右书写。)

但是,你更有可能看到这个例子变成了有关外部数据库副作用的有状态代码。在Haskell中,这将使用应用程序或monad的抽象,但大多数其他函数式语言不会成为数学形式主义的粘合剂。应用版本可以让你写像runValue $ completeOrder <$> withdrawFunds 2000 <$> addEmployees 2 <$> data。或者你可以把它写成do块。

Facebook为some real-world examples提供了如何为其部分数据库代码执行此操作。命令式代码:

NumCommonFriends(x, y) = Length(Intersect(FriendsOf(x), FriendsOf(y)))

有适用版本

numCommonFriends x y =
  length <$> (intersect <$> friendsOf x <*> friendsOf y)

这可以写成一些语法糖作为

numCommonFriends x y = do
  fx <- friendsOf x
  fy <- friendsOf y
  return (length (intersect fx fy))

以上是关于在函数式编程中,是否有一种干净的方法可以对某些数据执行许多操作,而无需将数据显式传递到每个函数中?的主要内容,如果未能解决你的问题,请参考以下文章

javascript进阶笔记

在 QBVideoChat 中暴露 AVAudioSession

强大的 Stream 函数式编程

解析:JavaScript中的函数式编程

大前端进击之路:函数式编程

对函数式编程语言的理解