线程资源的方法,如函数式编程中的连接



>假设您需要连接到数据库。

因此,您给出一个DbConnection作为某个假设函数的最后一个参数,类型如下:doDbStuff :: Int -> DbConnection -> Int

也许还有其他函数也依赖于DbConnection,并且它们都在执行写入操作。因此,它们可以单独运行,也可以作为原子操作(即事务)的一部分运行。

由于人们可能希望使用来管理DbConnection,并且函数可能是也可能不是原子操作的一部分,因此这些函数不会实现代码来从池中获取和释放DbConnection实例。

现在,这些功能是长函数组合的一部分,某些决策可能涉及不需要DbConnection。也就是说,有可能从池中获取DbConnection,并且它可能被另一个请求使用,这可能会产生瓶颈。

还有另一种选择,其中不会注入DbConnection而是像withConnection :: (DbConnection -> a) -> a这样的高阶函数,因此每个函数都可以接受DbConnection,使用它,整个withConnection负责获取和释放连接。这里的缺点是,很难使许多函数作为原子操作的一部分进行协作。

所以。。。

目前,我一直在使用#2方法。顺便说一句,有没有其他选择可以保留两种方法的优点?

JavaScript 中的伪代码:

方法#1

const connectionString = '[whatever]'
const env = { connection: acquire (connectionString) }
const output = composition (arg0) (argN) (env)
// then, release the connection
// f :: a -> b -> { connection: DbConnection }
const f = x => y => ({ connection }) => 
doDbStuff (x + y) (connection)

方法#2

const withConnection = f => [stuff to acquire the connection, and aftewards, release it]
const env = { withConnection }
const output = composition (arg0) (argN) (env)
// type FnConnection DbConnection c = c -> a
// f :: a -> a -> { connection: FnConnection }
const f = x => y => ({ withConnection }) => 
withConnection (doDbStuff (x + y))

有一个工具可以解决这种情况,您的解决方案非常接近它!读取器 adt 将允许您在访问某些环境的上下文中编写函数。这是我最喜欢的实现:https://github.com/monet/monet.js/blob/master/docs/READER.md

不幸的是,这种模式可能需要将大量代码包装在读取器类型中 - 但您已经引入了withConnection包装器,这会产生几乎相同数量的额外代码。

下面是一个示例,它从数据库中读取 id 为"123"的文档,覆盖某些属性,并将结果写回数据库。 提供数据库连接将推迟到实际运行程序之前,但您可以编写代码,假设运行代码时数据库连接将存在。

const { Reader } = require('monet');
const findById = (id) => Reader(({ db }) => db.find({ id }));
const insertDoc = (doc) => Reader(({ db }) => db.insert(doc));
const copyWithDefaults = (doc) => ({
...doc,
name: 'default name',
});
const app =
findById('123')
.map(copyWithDefaults)
.chain(insertDoc)
app.run({ db: aquire(connectionString) })

最新更新