gpt4 book ai didi

haskell - 使用 runReaderT 消除 MonadReader 约束

转载 作者:行者123 更新时间:2023-12-02 15:46:36 25 4
gpt4 key购买 nike

我一直在看Refactoring some Haskell code to use MTL它重构了一些 Haskell 代码以使用 mtl 包中的类型类。

该代码包含一个具有以下签名的 postReservation 函数:

postReservation :: ReservationRendition -> IO (HttpResult ())

postReservation 函数的实现使用了具有以下签名的三个附加函数:

readReservationsFromDB :: ConnectionString -> ZonedTime -> IO [Reservation]
getReservedSeatsFromDB :: ConnectionString -> ZonedTime -> IO Int
saveReservation :: ConnectionString -> Reservation -> IO ()

在视频中,三个函数的签名被重构,以便它们返回带有 MonadIO 约束的泛型类型,即

readReservationsFromDB :: (MonadIO m) => ConnectionString -> ZonedTime -> m [Reservation]
getReservedSeatsFromDB :: (MonadIO m) => ConnectionString -> ZonedTime -> m Int
saveReservation :: (MonadIO m) => ConnectionString -> Reservation -> m ()

我知道这样做可以使函数更加灵活,因为它们不再依赖于具体的 monad 类型或特定的 monad 转换器堆栈配置。我还了解到 postReservation 函数仍然可以使用这些函数,而无需对其类型签名进行任何更改,因为它的返回类型 IO 是 MonadIO 类型类的实例。

接下来,这三个函数被重构以包含 MonadReader 约束,以便不需要显式传递连接字符串,即

readReservationsFromDB :: (MonadReader ConnectionString m, MonadIO m) => ZonedTime -> m [Reservation]
getReservedSeatsFromDB :: (MonadReader ConnectionString m, MonadIO m) => ZonedTime -> m Int
saveReservation :: (MonadReader ConnectionString m, MonadIO m) => Reservation -> m ()

postReservation 函数的签名也已更新,以包含 MonadIOMonadReader 约束,即

postReservation :: (MonadReader ConnectionString m, MonadIO m) => ReservationRendition -> m (HttpResult ())

视频的演示者继续制作了名为 postReservationIOpostReservation 函数的具体版本,以消除类型类约束。编写 postReservationIO 函数的损坏版本是为了演示它不能仅使用 postReservation 函数,因为 IO 类型返回的是 postReservationIO 函数不是 MonadReader 类型类的实例。

然后我们被告知,为了消除 postReservationIO 函数中的 MonadReader 约束,我们需要使用 runReaderT 函数,该函数这是视频让我迷失的地方。

大约 15:00,postReservationIO 函数被重构为如下所示

postReservationIO :: ReservationRendition -> IO (Httpresult ())
postReservationIO req = runReaderT (postReservation req) connStr

runReaderT 函数具有 ReaderT k r m a -> r -> m a 的类型签名,我将其作为一个函数读取,该函数采用一些具体的 ReaderT 类型和一些 r 类型的值(在我们的例子中是连接字符串),它会给你一些 m a 类型的 monad。

postReservationIO 实现中,我们将 (postReservation req) 作为第一个参数传递给 runReaderT 函数。 (postReservation req) 的类型为

(MonadReader ConnectionString m, MonadIO m) => m (HttpResult ()) 

据我所知,这不是一个ReaderT,所以我很难理解它是如何工作的。

谁能解释一下我们如何从 (MonadReader ConnectionString m, MonadIO m) => m (HttpResult ()) 类型的内容跳转到 ReaderT k r m a为了消除 MonadReader 约束?

最佳答案

mpostReservation s 类型被实例化为 ReaderT * ConnectionString IO (HttpResult ()) ,这是 MonadReader ConnectionString 的实例和MonadIO .

请注意ReaderT仅通过 runReaderT 明确提及。正是该函数要求其参数是具体的 ReaderT而不是任意 MonadReader ConnectionString .

编辑:

正如 @Benjamin Hodgson 指出的,底层机制是返回类型多态性,或更普遍的统一。

所以,当 postReservationIO 的正文时经过类型检查,大致情况如下:

-- What we know, because we already type-checked them (this is necessary information about free variables):
runReaderT :: ReaderT k r m a -> r -> m a
postReservation req :: (MonadReader ConnectionString m', MonadIO m') => m' (HttpResult ())
connStr :: ConnectionString

-- What we want to check
runReaderT (postReservation req) connStr :: IO (HttpResult ())

-- Unifying `runReaderT` with its arguments results in the following constraints:
-- First argument
ReaderT k r m a ~ (MonadReader ConnectionString m', MonadIO m') => m' (HttpResult ())
-- Second argument
r ~ ConnectionString
-- Return type
m (HttpResult ()) ~ IO (HttpResult ())

阅读~因为“必须与……结合”。例如,runReaderT 的第二个参数是 ConnectionString需要类型变量 rConnectionString 统一.

约束ReaderT k r m a ~ (MonadReader ConnectionString m', MonadIO m') => m' (HttpResult ())这就是我之前提到的。这就是实例化 m'ReaderT * ConnectionString m ,进一步实例化为 ReaderT * ConnectionString IO作为最后一个约束的结果。

只有在满足所有类型变量约束后,GHC 才会检查 ReaderT * ConnectionString IO满足MonadReader ConnectionStringMonadIO ,确实如此。

如果情况并非如此,例如当postReservation :: (MonadLogger m, MonadIO m) => ReservationRendition -> m (HttpResult ()) ,那么编译器将无法找到实例 MonadLogger (ReaderT * ConnectionString IO)并提示。

关于haskell - 使用 runReaderT 消除 MonadReader 约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47392590/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com