Я работаю над проектом, который, среди прочего, включает в себя уровень доступа к базе данных. Довольно нормально, правда. В предыдущем проекте сотрудник предложил мне использовать концепцию Free Monads для уровня базы данных, и поэтому я это сделал. Теперь я пытаюсь решить в своем новом проекте, что я получу.
В предыдущем проекте у меня был API, который выглядел примерно так.
saveDocument :: RawDocument -> DBAction ()
getDocuments :: DocumentFilter -> DBAction [RawDocument]
getDocumentStats :: DBAction [(DocId, DocumentStats)]
и т.д.. Около 20 таких общественных функций. Чтобы поддержать их, у меня была структура данных DBAction
:
data DBAction a =
SaveDocument RawDocument (DBAction a)
| GetDocuments DocumentFilter ([RawDocument] -> DBAction a)
| GetDocumentStats ([(DocId, DocumentStats)] -> DBAction a)
| Return a
И затем реализация монады:
instance Monad DBAction where
return = Return
SaveDocument doc k >>= f = SaveDocument doc (k >>= f)
GetDocuments df k >>= f = GetDocuments df (k >=> f)
И затем переводчик. А затем примитивные функции, реализующие каждый из разных запросов. В принципе, я чувствую, что у меня было огромное количество кода клея.
В моем текущем проекте (в совершенно другом поле) я вместо этого пошел с довольно обычной монадой для моей базы данных:
newtype DBM err a = DBM (ReaderT DB (EitherT err IO) a)
deriving (Monad, MonadIO, MonadReader DB)
indexImage :: (ImageId, UTCTime) -> Exif -> Thumbnail -> DBM SaveError ()
removeImage :: DB -> ImageId -> DBM DeleteError ()
И так далее. Я полагаю, что в конечном итоге у меня будут "общедоступные" функции, которые представляют концепции высокого уровня, все из которых работают в контексте DBM
, а затем у меня будет целый набор функций, выполняющих клей SQL/Haskell. Это, в целом, намного лучше, чем свободная система монады, потому что я не пишу огромное количество кода шаблона, чтобы не получить ничего, кроме возможности поменять мой интерпретатор.
Или...
Я действительно получаю что-то еще с шаблоном Free Monad + Interpreter? Если да, то что?