【问题标题】:I know how to use it but I don't understand exactly how it does it (Reader monad)我知道如何使用它,但我不明白它是如何做到的(Reader monad)
【发布时间】:2014-08-24 20:51:49
【问题描述】:

考虑以下代码(省略了明显的部分)

main = do

    let s = "123456";
    let len = runReader calculateContentLength s
    putStrLn $ "Original 's' length: " ++ (show len)


calculateContentLength :: Reader String Int
calculateContentLength = do
    content <- ask           -- this seems to be the same as 'reader id'
    return (length content);

“询问”如何获取字符串参数?我的理解是因为类型声明

calculateContentLength :: Reader String Int

函数“calculateContentLength”有一个返回类型(Reader String Int 类型),但它没有传入参数。我意识到该函数本身只是传递给 runReader 函数的两个参数之一,但是 runReader 的第二个参数“s”究竟是如何与“calculateContentLength”中的“询问”相关联的?

换句话说,'calculateContentLength' 如何“知道”(并获得访问)'runReader' 传递的第二个参数?

【问题讨论】:

  • 顺便说一句,我确定涉及到柯里化,但我只是看不到细节。
  • 我鼓励您使用runReaderask 的源代码来证明简单的等式runReader ask s = s。然后看看你是否可以将证明用作直觉燃料。

标签: haskell monads reader-monad


【解决方案1】:

让我们看一下定义Reader的一种方法。

newtype Reader r a = Reader { runReader :: r -> a }

所以Reader 是一个接受函数的构造函数。该函数采用r 类型的环境,并返回a 类型的结果。

ask = Reader { runReader = \env -> env }
ask = Reader id

return 操作只是忽略环境并返回一个值。

return x = Reader { runReader = \_ -> x }

m &gt;&gt;= n 操作进行简单的排序:它获取环境,在该环境中运行 m,然后在同一环境中运行 n,并将 m 的结果传递给它。

m >>= n = Reader $ \env -> let
  a = runReader m env
  in runReader (n a) env

所以现在我们可以以你的例子为例,对其进行脱糖,并逐步减少它。

calculateContentLength = do
  content <- ask
  return (length content)

-- substitute definition of 'ask'

calculateContentLength = do
  content <- Reader id
  return (length content)

-- substitute definition of 'return'

calculateContentLength = do
  content <- Reader id
  Reader (\_ -> length content)

-- desugar 'do' into '>>='

calculateContentLength =
  Reader id >>= \content -> Reader (\_ -> length content)

-- definition of '>>='

calculateContentLength = Reader $ \env -> let
  a = runReader (Reader id) env
  in runReader ((\content -> Reader (\_ -> length content)) a) env

-- reduce lambda

calculateContentLength = Reader $ \env -> let
  a = runReader (Reader id) env
  in runReader (Reader (\_ -> length a)) env

-- definition of 'runReader'

calculateContentLength = Reader $ \env -> let
  a = id env
  in runReader (Reader (\_ -> length a)) env

-- definition of 'id'

calculateContentLength = Reader $ \env -> let
  a = env
  in runReader (Reader (\_ -> length a)) env

-- remove redundant variable

calculateContentLength = Reader $ \env
  -> runReader (Reader (\_ -> length env)) env

-- definition of 'runReader'

calculateContentLength = Reader $ \env -> (\_ -> length env) env

-- reduce

calculateContentLength = Reader $ \env -> (length env)
calculateContentLength = Reader length

现在应该更容易看出runReader calculateContentLengthlength 的相同之处,以及ask 并不神奇——monad 的&gt;&gt;= 操作构建了一个函数,当您使用runReader 运行计算。

实际上,Reader 是根据ReaderT 定义的,它使用一元动作而不是纯函数,但其​​实现形式基本相同。

【讨论】:

  • 谢谢 --- 我完全忘记了函数开头的“do”。所以我错过了整个绑定部分。这就是为什么我看不到发生了什么。谢谢你的详细解释。
猜你喜欢
  • 2023-01-14
  • 2017-06-22
  • 2021-07-10
  • 2015-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-11
相关资源
最近更新 更多