Haskell,ask 的参数

0 monads haskell

你能帮助得到什么参数ask吗?我们经常可以看到ask >>= f 这意味着ask >>= f = (\k -> f (ask k) k)
So ask 必须能够从环境中得到 k,函数。
但是,在文档中它是这样写的:ask :: m r. 我哪里错了?

sin*_*law 5

一个类型m awhere mis a Monad 的值可以被认为是一个“monadic action”。所以ask不带任何参数,它只是一个值,您可以绑定 ( >>=) 以从 Reader monad 中提取一些值。

看一下askfor ReaderTin的定义Control.Monad.Trans.Reader

-- | Fetch the value of the environment.
ask :: (Monad m) => ReaderT r m r
ask = ReaderT return
Run Code Online (Sandbox Code Playgroud)

ReaderT仅仅是一个包含类型的值的数据构造r -> m a,因此ReaderT return是类型的值ReaderT r m r包含一个函数,return(的单子m)。

换句话说,ask这是一个“一元动作”,它提取存储在Reader.

ask >>= f 
Run Code Online (Sandbox Code Playgroud)

这是

(ReaderT return) >>= f
Run Code Online (Sandbox Code Playgroud)

使用>>=for Reader 的定义,我们得到:

ReaderT $ \ r -> do
    a <- runReaderT (ReaderT return) r
    runReaderT (f a) r
Run Code Online (Sandbox Code Playgroud)

这减少到

ReaderT $ \ r -> do
    a <- return r
    runReaderT (f a) r
Run Code Online (Sandbox Code Playgroud)

或者

ReaderT $ \r -> runReaderT (f r) r
Run Code Online (Sandbox Code Playgroud)

因此,它传递存储的值以决定下一个操作,并传递该值以便下一个操作可以像以前一样读取它。

(如果这还不清楚,请寻找阅读器教程)


Lui*_*las 5

这是Reader单子。最终,最好的答案就是研究它的实现,最简单的版本(没有 monad 转换器,没有类)可以这样定义:

\n\n
newtype Reader r a = Reader { runReader :: r -> a }\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是一个newtype声明,因此Reader r a只是函数类型的“重新标记”(可以这么说)r -> aask定义如下:

\n\n
ask :: Reader r r\nask = Reader (\\r -> r)\n
Run Code Online (Sandbox Code Playgroud)\n\n

这意味着这ask是一个重新标记的恒等函数\xe2\x80\x94,该函数仅返回其自己的参数。如果我们使用runReader如果我们使用该操作向其提供值,

\n\n
ghci> runReader ask 5\n5\n\nghci> runReader ask "Hello world!"\n"Hello world!"\n
Run Code Online (Sandbox Code Playgroud)\n\n

这看起来不太有用,但神奇之处在于它ReaderFunctor,Applicative和 的实例Monad实例:

\n\n
instance Functor (Reader r) where\n  fmap f (Reader g) =\n      -- A `Reader` that applies `f` to the original `Reader`\'s results \n      Reader (\\r -> f (g r))\n\ninstance Applicative (Reader r) where\n  pure a = \n      -- A `Reader` that ignores its `r` argument and just produces\n      -- a fixed value.\n      Reader (\\_ -> a)\n\n  Reader ff <*> Reader fa =\n      -- A `Reader` that "combines" two `Reader`s by feeding the same \n      -- `r` value to both, and then combining their results \n      Reader (\\r -> ff r (fa r))\n\ninstance Monad (Reader r) where\n  return = pure\n\n  Reader fa >>= k = \n      -- A `Reader` that feeds the same `r` both to the original one\n      -- and to the one computed by the `k` function\n      Reader (\\r -> k (fa r) r)\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您研究这些,您会注意到,这Reader延迟程序中将包装器r -> a函数应用于r. 通常,如果您有一个类型为 的函数r -> a并且想要获取类型为 的值a,则必须向该函数提供类型为 的参数r。类Reader实例允许您提供用于提前操作的函数a,然后提供r

\n\n

类型ReaderTMonadReader类(其中有ask :: MonadReader r m => m r方法)只是其更高级的版本。

\n