2017-10-13 90 views
9

我在阅读Purescript通过示例并介绍了读者单元的部分。示例如下:了解阅读器monad

createUser :: Reader Permissions (Maybe User) 
createUser = do 
    permissions <- ask 
    if hasPermission "admin" permissions 
    then map Just newUser 
    else pure Nothing 

对我而言,令人困惑的部分是ask函数。签名是:

ask :: forall r. Reader r r 

它看起来好像它创建了一个读者凭空

当我念叨State单子,它与它的get功能相同的概念。并解释如下文字:

该状态是由状态monad的数据构造函数隐藏的函数参数实现的,所以没有明确的引用来传递。

我猜这是关键,而同样的事情,与读者发生在这里,但我不明白它是如何工作...

在上面的例子中运行通过runReader,如何提供的值是否突然显示为ask的结果? Haskell文档ask说:检索monad环境。但我的困惑是从哪里?我看到它的方式,价值传递到runReader,得到存储某处,并得到它 - 你叫ask ...但这是没有意义的。

虽然示例是Purescript,但我猜猜任何Haskell识字人员也能够回答,因此Haskell标签。

+2

'Reader a b'是'a - > b'的一个包装。所以,问问::。读者a'最终只是'forall a'类型的一个值。 a - > a',并在其周围包装。 –

回答

8

我目前没有PureScript环境,所以我会尝试从Haskell的角度回答,并希望它有所帮助。

一个读者真的只是一个围绕一个函数“包装”,所以当你得到一个Reader r r,你真的只从rr吸引读者;换句话说,功能r -> r

可以召唤功能凭空的,因为,如果你是一个柏拉图主义者,我想他们永远存在......

当您使用do符号,你是“单子”内,所以上下文r是隐含的。换句话说,您调用返回r值的函数,并且当您使用箭头时,您只需获取该上下文。

+2

Upvoted。从稀薄的空气中召唤这样一个“r→r”功能的方式可能不值一提,就是说出咒语“id”。 (而'id'是_only_这样的函数,这要归功于参数性。) –

+0

好的,所以在我的情况下,Reader是Permissions - > Maybe User的包装。当我运行'runreader createUser permissions'时,'createUser'正文中的'ask'如何知道返回与我传递给'runReader'相同的'permissions'?我敢肯定,这个问题是完全荒谬的,因为我看着这一切都是错误的......但请尝试帮助我伸直我的大脑。 – kaqqao

+1

@kaqqao'Reader Permissions(Maybe User)'只是'Permissions - >(Maybe User)'上的'包装器',所以你的整个'createUser''值'实际上是一个函数(但函数是值,所以这很酷)。当你调用'runReader'时,你不仅必须传递'createUser',而且还要传递'r'的值 - 在这种情况下是'Permissions'值。 'runReader'然后用你传递的'Permissions'值来调用包装函数。 HTH。 –

1

你可以通过执行一些替换来说服你自己,它的工作原理。首先看看createUser的签名。让我们“展开”的Reader定义:

createUser :: Reader Permissions (Maybe User) 
{- definition of Reader -} 
createUser :: ReaderT Permissions Identity (Maybe User) 

ReaderT类型只有一个数据构造:ReaderT (r -> m a),这意味着createUser是计算结果为ReaderT (Permissions -> Identity (Maybe User))类型的值的术语。正如你所看到的,它只是一个标有ReaderT的函数。它无需凭空创建任何内容,但在调用该函数时将获得类型Permissions的值。

现在我们来看看您遇到的问题。你知道do符号只是语法糖,并表达:

do permissions <- ask 
    if hasPermission "admin" permissions 
    then map Just newUser 
    else pure Nothing 

desugars到

ask >>= \permissions -> 
    if hasPermission "admin" permissions 
    then map Just newUser 
    else pure Nothing 

要理解这是什么一样,你将不得不查找ask>>=pure定义为ReaderT。让我们进行另一轮取代:

ask >>= \permissions -> ... 
{- definition of ask for ReaderT -} 
ReaderT pure >>= \permissions -> ... 
{- definition of >>= for ReaderT -} 
ReaderT \r -> 
    pure r >>= \a -> case (\permissions -> ...) a of ReaderT f -> f r 
{- function application -} 
ReaderT \r -> 
    pure r >>= \a -> 
    case (if hasPermission "admin" a 
      then map Just newUser 
      else pure Nothing) of ReaderT f -> f r 
{- definition of pure for Identity -} 
ReaderT \r -> 
    Identity r >>= \a -> 
    case (if hasPermission "admin" a 
      then map Just newUser 
      else pure Nothing) of ReaderT f -> f r 
{- definition of >>= for Identity -} 
ReaderT \r -> 
    (\a -> 
    case (if hasPermission "admin" a 
      then map Just newUser 
      else pure Nothing) of ReaderT f -> f r) r 
{- function application -} 
ReaderT \r -> 
    case (if hasPermission "admin" r 
     then map Just newUser 
     else pure Nothing) of ReaderT f -> f r 

正如你所看到的,createUser显然只是ReaderT的线程通过你的表达式值(“环境”)包裹的功能。 runReader解开函数并用提供的参数调用它:

runReader :: forall r a. Reader r a -> r -> a 
runReader (ReaderT f) r = f r