Подход к ресурсам потоков, таким как соединения в функциональном программировании - PullRequest
0 голосов
/ 11 января 2019

Допустим, вам нужно подключиться к базе данных.

Итак, вы даете 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))

1 Ответ

0 голосов
/ 12 июля 2019

Существует инструмент для этой ситуации, и ваше решение очень близко к нему! Читатель adt позволит вам написать ваши функции в контексте наличия доступа к некоторой среде. Вот моя любимая реализация: https://github.com/monet/monet.js/blob/master/docs/READER.md

К сожалению, для этого шаблона может потребоваться перенос больших объемов кода в тип читателя, но вы уже внедрили оболочку withConnection, что приводит к тому же количеству дополнительного кода.

Вот пример, который читает документ с идентификатором «123» из БД, переопределяет некоторые свойства и записывает результат обратно в БД. предоставление соединения 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) })
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...