gpt4 book ai didi

haskell - 对Haskell Monad变形金刚感到困惑

转载 作者:行者123 更新时间:2023-12-01 00:16:57 27 4
gpt4 key购买 nike

我对m应该放在Monad变压器右侧的位置感到困惑吗?

例如:

WriterT定义为

newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }


ReaderT被定义为

newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }


但不是

newtype ReaderT r m a = ReaderT { runReaderT :: m (r -> a) }

最佳答案

monad m的位置将取决于应用于基础monad m的monad转换器的功能和操作,因此,它由读写器应该添加到monad的功能确定。

记住runReaderTrunWriterT尽管具有暗示性的名称,实际上并没有做任何事情。他们只是解包一个新类型,正是它们包装的东西改变了monad m

我的意思是,给定monad m,您可以通过考虑类型的monadic动作向其添加读者:

r -> m a


并且您可以通过考虑以下类型的单子动作向其添加编写器:

m (a, w)


并且您可以通过考虑以下类型的单子动作来向其添加阅读器,作者和状态:

r -> s -> m (a, s, w)


(也就是说,您不需要任何变压器包装器,尽管它们可以使它更加方便,尤其是因为您可以使用现有的运算符,例如 >>=<*>,而不必定义自己的运算符。)

因此,当您将阅读器添加到monad m时,为什么不将 m放在开头并考虑以下类型的monadic动作呢?

m (r -> a)


实际上,您可以这样做,但是您很快就会发现,这种添加阅读器的方法实际上并没有为monad m添加太多功能。

例如,假设您正在编写一个应在值表中查找键的函数,并且希望将该表携带在读取器中。由于查找可能失败,因此您想在 Maybe monad中执行此操作。因此,您想要编写如下内容:

myLookup :: Key -> Maybe Value
myLookup key = ...


但是,您想使用提供键和值表的阅读器来增强 Maybe monad。如果使用 m (r -> a)模式执行此操作,则会得到:

myLookup :: Key -> Maybe ([(Key,Value)] -> Value)


现在,让我们尝试实现它:

myLookup k = Just (\tbl -> ...)


我们已经看到了一个问题。在允许我们编写代码以访问 Just之前,我们必须提供一个 \tbl(指示查找成功)。也就是说,单例操作(失败或带有返回值的成功)不能依赖于 r中的信息,这些信息从签名 m (r -> a)中应该是显而易见的。使用备用 r -> m a模式更强大:

type M a = ([Key,Value]) -> Maybe a
myLookup :: Key -> M Value
myLookup key tbl = Prelude.lookup key tbl


@Thomas_M_DuBuisson给出了另一个示例。如果我们尝试读取输入文件,则可以编写:

readInput :: FilePath -> IO DataToProcess
readInput fp = withFile fp ReadMode $ \h -> ...


最好在阅读器中随身携带诸如文件路径之类的配置信息,所以让我们使用模式 m (r -> a)将其转换为:

data Config = Config { inputFile :: FilePath }
readConfig :: IO (Config -> DataToProcess)
readConfig = ...um...


并且因为无法编写依赖于配置信息的IO操作而陷入困境。如果我们使用替代模式 r -> m a,则将设置为:

type M a = Config -> IO a
readConfig :: M DataToProcess
readConfig cfg = withFile (inputFile cfg) ReadMode $ ...


@cdk引发的另一个问题是这种新的“单子”动作类型:

m (r -> a)


甚至都不是单子。它较弱(仅适用于应用)。

请注意,将仅适用的阅读器添加到monad仍然有用。它只需要用于计算结构不依赖 r中的信息的计算中即可。 (因此,如果基础monad为 Maybe以允许计算发出错误信号,则可以将 r中的值用于计算中,但确定计算是否成功必须独立于 r。 )

但是, r -> m a版本严格更强大,可以用作单例和应用程序阅读器。

请注意,一些单调变换可以多种形式使用。例如,您可以(但仅在某些情况下,如@luqui在评论中指出的那样)以两种方式将作家添加到 m monad中:

m (a, w)  -- if m is a monad this is always a monad
(m a, w) -- this is a monad for some, but not all, monads m


如果 mIO,则 IO (a,w)(IO a, w)有用-对于后者,写入的 w(例如,错误日志)不能取决于执行< cc>行动!同样, IO实际上不是单子。这只是一个应用。

另一方面,如果 (IO a, w)m,则 Maybe会写一些内容,无论计算成功还是失败,而 (Maybe a, w)如果返回 Maybe (a, w),则会丢失所有日志条目。两种形式都是单子形式,可以在不同情况下使用,它们分别对应于以不同顺序堆叠变压器:

MaybeT (Writer w)  -- acts like  (Maybe a, w)
WriterT w Maybe -- acts like Maybe (a, w)


对于以不同顺序堆叠 NothingMaybe而言,情况并非如此。这两个都是“好”阅读器 Reader的同构:

MaybeT (Reader r)
ReaderT r Maybe

关于haskell - 对Haskell Monad变形金刚感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51864209/

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