4

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

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);

'ask' 如何获取字符串参数?我的理解是因为类型声明

calculateContentLength :: Reader String Int

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

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

4

1 回答 1

12

让我们看一种定义Reader.

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

Reader接受函数的构造函数也是如此。该函数采用 type 的环境r,并返回 type 的结果a

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

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

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

m >>= 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 calculateContentLength与 just 有何相同length,以及如何ask不神奇——monad 的>>=操作构建了一个函数,当您使用 运行计算时,该函数会为您隐式传递环境runReader

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

于 2014-08-24T21:15:11.697 回答