Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 更喜欢读卡器monad而不是直接将环境作为参数传递_Haskell_Monads_Reader Monad - Fatal编程技术网

Haskell 更喜欢读卡器monad而不是直接将环境作为参数传递

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是

我正在用Haskell编写一个基本的CRUD应用程序,使用库服务和Opaleye

Servant设置API端点,Opalye将数据存储在DB中

假设有一个端点
GET/users
返回数据库中所有用户的列表,另一个端点
POST/user
创建新用户并将其保存在数据库中

程序通过启动到DB的连接来运行,然后将此连接作为参数传递给这些API端点函数(使用SERVIDER设置),作为参数

有人建议我,更好的方法是使用阅读器Monad并将连接存储在环境中

我能够做到,但我不明白为什么Reader Monad是共享环境的首选方式,而不是直接传递参数

另外,作为Haskell的初学者,我可以使用Monads,遵循教程,让我的程序运行,但我真的不知道它们背后隐藏的美丽数学。这就是为什么我想避免使用单子(直到我完全理解单子背后的思想)

这是我的,顺便说一句

  • 当您希望将参数更深地传递到调用堆栈中时,Monad Reader更方便

  • Monad读取器方便代码更改/扩展。假设您想从数据库中获取一些类型为
    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)
    相当不理解。