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

问题描述 投票:1回答:2

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

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是否有意义?

functional-programming monads
2个回答
1
投票

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

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)))

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


0
投票

你会使用作文。在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))
© www.soinside.com 2019 - 2024. All rights reserved.