更喜欢 Reader monad 而不是直接将环境作为参数传递
Preferring Reader monad over directly passing the environment as parameters
我在 Haskell 中使用库 Servant 和 Opaleye 编写了一个基本的 CRUD 应用程序。
Servant 设置 API 端点和 Opaleye 将数据存储在数据库中。
假设有一个端点 GET /users
,它 returns 来自数据库的所有用户的列表,另一个端点 POST /user
创建一个新用户并将其保存在数据库中。
程序通过启动与数据库的连接,然后将此连接作为参数传递给这些 API 端点函数(使用 Servant 设置)作为参数。
有人向我推荐更好的方法是使用 Reader Monad 并将连接存储在环境中。
我能够做到,但我不明白为什么 Reader Monad 是共享环境而不是直接传递参数的首选方式。
P.S。 - 作为 Haskell 的初学者,我可以使用 Monads,遵循教程并编写我的程序 运行,但我并不真正了解它们背后隐藏的美丽数学。这就是为什么,我想避免使用 monad(直到我完全理解 monad 背后的想法)。
这是我的 code,顺便说一句。
Monad Reader 更方便,当你想将参数传递到调用堆栈的更深层次时。
Monad Reader 简化代码 change/extension。假设你想从数据库中获取一些 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
window 经理。某处有 XConfig
数据类型
在 X
monad 的内部,但大多数时候我不想知道它,不要管它。
我在 Haskell 中使用库 Servant 和 Opaleye 编写了一个基本的 CRUD 应用程序。
Servant 设置 API 端点和 Opaleye 将数据存储在数据库中。
假设有一个端点 GET /users
,它 returns 来自数据库的所有用户的列表,另一个端点 POST /user
创建一个新用户并将其保存在数据库中。
程序通过启动与数据库的连接,然后将此连接作为参数传递给这些 API 端点函数(使用 Servant 设置)作为参数。
有人向我推荐更好的方法是使用 Reader Monad 并将连接存储在环境中。
我能够做到,但我不明白为什么 Reader Monad 是共享环境而不是直接传递参数的首选方式。
P.S。 - 作为 Haskell 的初学者,我可以使用 Monads,遵循教程并编写我的程序 运行,但我并不真正了解它们背后隐藏的美丽数学。这就是为什么,我想避免使用 monad(直到我完全理解 monad 背后的想法)。
这是我的 code,顺便说一句。
Monad Reader 更方便,当你想将参数传递到调用堆栈的更深层次时。
Monad Reader 简化代码 change/extension。假设你想从数据库中获取一些
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
window 经理。某处有 XConfig
数据类型
在 X
monad 的内部,但大多数时候我不想知道它,不要管它。