Haskell 更喜欢读卡器monad而不是直接将环境作为参数传递
我正在用Haskell编写一个基本的CRUD应用程序,使用库服务和Opaleye Servant设置API端点,Opalye将数据存储在DB中 假设有一个端点Haskell 更喜欢读卡器monad而不是直接将环境作为参数传递,haskell,monads,reader-monad,Haskell,Monads,Reader Monad,我正在用Haskell编写一个基本的CRUD应用程序,使用库服务和Opaleye Servant设置API端点,Opalye将数据存储在DB中 假设有一个端点GET/users返回数据库中所有用户的列表,另一个端点POST/user创建新用户并将其保存在数据库中 程序通过启动到DB的连接来运行,然后将此连接作为参数传递给这些API端点函数(使用SERVIDER设置),作为参数 有人建议我,更好的方法是使用阅读器Monad并将连接存储在环境中 我能够做到,但我不明白为什么Reader Monad是
GET/users
返回数据库中所有用户的列表,另一个端点POST/user
创建新用户并将其保存在数据库中
程序通过启动到DB的连接来运行,然后将此连接作为参数传递给这些API端点函数(使用SERVIDER设置),作为参数
有人建议我,更好的方法是使用阅读器Monad并将连接存储在环境中
我能够做到,但我不明白为什么Reader Monad是共享环境的首选方式,而不是直接传递参数
另外,作为Haskell的初学者,我可以使用Monads,遵循教程,让我的程序运行,但我真的不知道它们背后隐藏的美丽数学。这就是为什么我想避免使用单子(直到我完全理解单子背后的思想)
这是我的,顺便说一句
Foo
的值,更新它(以不纯净的方式)并将其存储回数据库。这里有两个版本,带有读取器
和显式参数传递
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
datafoo=。。。
modifyFoo::Foo->IO Foo
类型处理程序a=读卡器连接IO a
fetch1::Connection->Int->IO Foo
fetch2::Int->Handler Foo
store1::Connection->Foo->IO()
store2::Foo->Handler()
修改1::连接->整数->IO()
modify1 conn key=do
上一页。修改>=>store2
fetch2
和store2
将参数从连接更改为其他(或更大)参数,您只需更新Handler
类型别名,modify2
保持不变。
如果modify1
,Connection
在类型签名中是显式的,您也必须更改它
有关使用读取器的另一个示例
我建议使用xmonad
窗口管理器。某个地方有XConfig
数据类型
在
X
monad的内部,但大多数时候我不想知道它,更别说把它传下去了。切题说明:“这就是为什么,我想避免使用monad(直到我完全理解monad背后的思想)。”——我建议你改用它们。美丽的数学可以等待。事实上,使用单子。并非所有经验丰富的Haskeller都喜欢使用Reader
,而不是传递参数:@danidiaz是否有任何W使得所有经验丰富的W用户都喜欢使用X来做Y?当然,大多数事情都有权衡。在我看来,Reader
@leftaroundabout的好处往往更大。你能用好处/成本分析写一个答案吗?实际上,“使用monads是最好的路径…”是一个很好的论点,尽管我想知道还有什么其他的优点和缺点。就像使用Handler
,您可以将连接
作为一个类型别名(例如modify1::Handler->Int->IO
),因此在#2中没有好处/代价。而且#1有点模糊,请你详细说明一下。我同意#2,除了。。。如果是项目定义的数据类型,则可以将别名设置为连接
。如果数据Connection
来自某个库,将其设为别名将导致混淆:type Connection=(Lib.Connection,Bar)
相当不理解。