Как вы структурируете модуль с отслеживанием состояния в Haskell?

Я хочу написать общий модуль, который позволит программам на Haskell взаимодействовать с Cassandra. Модуль должен будет поддерживать собственное состояние. Например, у него будет пул соединений и список обратных вызовов, которые будут вызываться при сохранении новой записи. Как мне структурировать код, чтобы этот модуль мог поддерживать свое состояние? Вот некоторые из рассмотренных мной подходов. Я на правильном пути? (Я новичок в Haskell и все еще изучаю лучшие способы функционального мышления.)

Вариант 1:

Модуль работает в монаде (StateT s IO), где s - глобальное состояние для всей программы, использующей модуль Cassandra. Конечно, поскольку модуль Cassandra может использоваться несколькими программами, детали того, что находится в s, должны быть невидимы для модуля Cassandra. Модуль должен был бы экспортировать класс типа, который позволил бы ему извлечь CassandraState из s и отправить новый CassandraState обратно в s. Тогда любая программа, использующая модуль, должна будет сделать свое основное состояние членом этого типа class.

Вариант 2:

Модуль работает в монаде (StateT CassandraState IO). Каждый раз, когда кто-то вызывает действие в модуле, ему придется извлекать CassandraState из того места, где оно было припрятано, вызывать действие с помощью runState, брать полученное состояние и снова скрывать его (где угодно).

Вариант 3 :

Не помещайте функции модуля Cassandra в монаду StateT вообще. Вместо этого попросите вызывающего абонента явно передать CassandraState ' при необходимости. Проблема с вариантом 2 заключается в том, что не все функции в модуле изменят состояние. Например, получение соединения изменит состояние и потребует от вызывающего спрятать полученное состояние. Но для сохранения новой записи необходимо прочитать состояние (чтобы получить обратные вызовы), но изменять состояние не нужно. Вариант 2 не дает вызывающей стороне никаких подсказок, что connect изменяет состояние, а create - нет.

Но, если я откажусь от использования монады StateT и буду иметь только функции, которые принимают состояния в качестве параметров и возвращают либо простые значения или кортежи простых значений и новых состояний, тогда для вызывающей стороны действительно очевидно, что состояние необходимо сохранить. (Под обложками в моем модуле я бы взял входящие состояния и встроил их в монаду (StateT CassandraState IO), но подробности этого будут скрыты от звонящего. Таким образом, для вызывающего абонента интерфейс очень ясен, но, по сути, это просто Вариант 2.)

Вариант 4:

Что-то еще?

Эта проблема должна возникать довольно часто при создании повторно используемых модулей. Есть ли какой-нибудь стандартный способ решить эту проблему?

(Кстати, если кто-то знает лучший способ взаимодействия с Кассандрой из Haskell, чем использование Thrift, дайте мне знать! Возможно, мне не нужно писать это на все.: -)

9
задан Clint Miller 24 January 2011 в 18:01
поделиться