假设您需要连接到数据库。
因此,您将DbConnection
作为某些假设函数的最后一个参数,其类型如下:doDbStuff :: Int -> DbConnection -> Int
也许还有其他功能也依赖于DbConnection
,并且它们都在执行写操作。因此,它们可以单独运行,也可以作为原子操作的一部分(即事务)运行。
由于人们可能想使用 pool 管理DbConnection
,而函数可能会或可能不是原子操作的一部分,因此这些函数不会实现获取和释放{{ 1}}实例与池之间。
现在,这些功能是长功能组合的一部分,某些决定可能涉及这些功能,不需要DbConnection
。也就是说,很有可能会将DbConnection
从池中取出,并可能被另一个请求使用,这可能会产生瓶颈。
有另一种选择,其中一个不会注入DbConnection
,而是一个像DbConnection
这样的高阶函数,因此每个函数都可以使用一个withConnection :: (DbConnection -> a) -> a
,并使用它和整个{{ 1}}负责获取和释放连接。这样做的缺点是,很难将许多功能作为原子操作的一部分进行协作。
目前,我一直在使用#2方法。顺便说一句,是否有其他方法可以保留两种方法中的最佳方法?
JavaScript中的伪代码:
DbConnection
withConnection
答案 0 :(得分:1)
有一种针对这种情况的工具,您的解决方案非常接近!读者adt允许您在访问某些环境的情况下编写函数。这是我最喜欢的实现:https://github.com/monet/monet.js/blob/master/docs/READER.md
不幸的是,此模式可能需要将大量代码包装在阅读器类型中-但是您已经引入了withConnection包装器,这导致几乎相同数量的额外代码。
这里是一个示例,该示例从db中读取ID为“ 123”的文档,覆盖某些属性,然后将结果写回到db中。提供的db连接将延迟到实际运行您的程序之前,但是您可以编写代码,假设运行代码时将存在db连接。
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) })