3

我正在使用库 Servant 和 Opaleye 在 Haskell 中编写一个基本的 CRUD 应用程序。

Servant 设置 API 端点和 Opaleye 以将数据存储在 DB 中。

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

该程序通过启动与 DB 的连接,然后将此连接作为参数传递给这些 API 端点函数(使用 Servant 设置)作为参数。

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

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

PS - 作为 Haskell 的初学者,我可以使用 Monads,按照教程运行我的程序,但我并不真正了解它们背​​后隐藏的美丽数学。这就是为什么,我想避免使用单子(直到我完全理解单子背后的想法)。

这是我的代码,顺便说一句。

4

1 回答 1

3
  1. 当您想将参数传递到调用堆栈更深的几个级别时,Monad Reader 更加方便。

  2. 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在 monad 内部的某个地方有数据类型X,但大多数时候我不想知道它,别管它了。

于 2018-06-15T16:48:23.233 回答