Предпочитая монаду Reader по прямой передаче среды в качестве параметров
Я писал базовое приложение CRUD на Haskell, используя библиотеки Servant и Opaleye.
Слуга для настройки конечных точек API и Opaleye для хранения данных в БД.
Допустим, есть конечная точка GET /users
который возвращает список всех пользователей из БД и другой конечной точки POST /user
который создает нового пользователя и сохраняет его в БД.
Программа создается установлением соединения с БД, а затем передаёт это соединение в качестве параметра этим функциям конечных точек API (настройка с использованием Servant) в качестве параметра.
Кто-то порекомендовал мне, что лучше использовать Reader Monad и хранить соединение в среде.
Я смог это сделать, но я не понимаю, почему Reader Monad является предпочтительным способом совместного использования среды, а не прямой передачи аргументов.
PS - Будучи новичком в Haskell, я могу использовать Monads, следовать учебным пособиям и запускать мою программу, но я действительно не знаю, какая скрытая математика стоит за ними. Вот почему я хочу избегать использования монад (до тех пор, пока я полностью не пойму идею, лежащую в основе монад).
Вот мой код, кстати.
1 ответ
Monad Reader более удобен, когда вы хотите передавать аргументы на несколько уровней глубже в стек вызовов.
Monad Reader облегчает изменение / расширение кода. Предположим, вы хотите получить какое-то значение типа
Foo
из базы данных, обновить его (нечистым образом) и сохранить его обратно. Вот две версии, сReader
и с явной передачей аргументов.data Foo = ... modifyFoo :: Foo -> IO Foo type Handler a = Reader Connection IO a fetch1 :: Connection -> Int -> IO Foo fetch2 :: Int -> Handler Foo store1 :: Connection -> Foo -> IO () store2 :: Foo -> Handler () modify1 :: Connection -> Int -> IO () modify1 conn key = do prev <- fetch1 conn key new <- modify prev store1 conn new modify2 :: Int -> Handler () modify2 key = do prev <- fetch2 key new <- liftIO $ modify prev store2 new -- for brave souls modify2' :: Int -> Handler () modify2' = fetch2 >=> liftIO . modify >=> store2
Должен ли когда-нибудь fetch2
а также store2
измените аргумент с Connection на что-то другое (или большее), вы просто обновите Handler
введите псевдоним, modify2
остается такой же. В случае modify1
, Connection
явным образом указывается в сигнатуре типа, вам также придется ее изменить.
Для другого примера использования Reader
Я бы предложил xmonad
оконный менеджер. Есть XConfig
тип данных где-то во внутренних органах X
монада, но большую часть времени я не хочу это знать, оставь в покое.