简体   繁体   English

读者Monad澄清

[英]Reader Monad clarification

I am trying to make sense of the reader monad but can't seem to grasp what bind (>>=) does in this monad. 我试图理解读者monad但似乎无法理解bind(>> =)在这个monad中的作用。

Here's the implementation I am analyzing: 这是我正在分析的实现:

newtype Reader e a = Reader { runReader :: (e -> a) }

instance Monad (Reader e) where 
    return a         = Reader $ \e -> a 
    (Reader r) >>= f = Reader $ \e -> runReader (f (r e)) e
  1. My first question is, why is Reader partially applied on the left hand side of bind? 我的第一个问题是,为什么Reader部分应用于绑定的左侧? (Reader r) instead of (Reader ra) . (Reader r)代替(Reader ra)
  2. What goes on in this part of the definition: (f (re)) , what is it's purpose? 在这部分定义中发生了什么: (f (re)) ,它的目的是什么?

Thanks a lot for helping me out. 非常感谢帮助我。

My first question is, why is Reader partially applied on the left hand side of bind? 我的第一个问题是,为什么Reader部分应用于绑定的左侧? (Reader r) instead of (Reader ra) . (Reader r)代替(Reader ra)

It isn't. 事实并非如此。 That use of Reader is fully saturated, as it must be. Reader使用完全饱和,必须如此。 However, I can understand your confusion… remember that in Haskell, types and values are in different namespaces, and defining a datatype with data or newtype brings new names into scope in both namespaces. 但是,我可以理解你的困惑......请记住,在Haskell中,类型和值位于不同的名称空间中,并且使用datanewtype定义数据类型会在两个名称空间中将新名称带入范围。 For example, consider the following declaration: 例如,请考虑以下声明:

data Foo = Bar | Baz

This definition binds three names, Foo , Bar , and Baz . 这个定义绑定了三个名字, FooBarBaz However, the part on the left-hand side of the equals sign is bound in the type namespace, since Foo is a type, and the constructors on the right hand side are bound in the value namespace, since Bar and Baz are essentially values. 但是,等号左侧的部分绑定在类型命名空间中,因为Foo是一个类型,右侧的构造函数绑定在值命名空间中,因为BarBaz本质上是值。

All of these things have types as well, which can be helpful to visualize. 所有这些东西都有类型,这有助于可视化。 Foo has a kind , which is essentially the “type of a type-level thing”, and Bar and Baz both have a type. Foo一种 ,实质上是“类型级别的东西”,而BarBaz都有一种类型。 These types can be written as follows: 这些类型可以写成如下:

Foo :: *
Bar :: Foo
Baz :: Foo

…where * is the kind of types. ......其中*是那种类型。

Now, consider a slightly more complicated definition: 现在,考虑一个稍微复杂的定义:

data Foo a = Bar Integer String | Baz a

Once again, this definition binds three names: Foo , Bar , and Baz . 再一次,这个定义绑定了三个名字: FooBarBaz Once again, Foo is in the type namespace and Bar and Baz are in the value namespace. Foo再次位于类型命名空间中, BarBaz位于值命名空间中。 Their types, however, are more elaborate: 然而,它们的类型更精细:

Foo :: * -> *
Bar :: Integer -> String -> Foo a
Baz :: a -> Foo a

Here, Foo is a type constructor, so it's essentially a type-level function that accepts a type ( * ) as an argument. 这里, Foo是一个类型构造函数,因此它本质上是一个类型级函数,它接受一个类型( * )作为参数。 Meanwhile, Bar and Baz are value-level functions that accept various values as arguments. 同时, BarBaz是接受各种值作为参数的价值级函数。

Now, return to the definition of Reader . 现在,回到Reader的定义。 Avoiding record syntax for a moment, we can reformulate it as follows: 暂时避免使用记录语法,我们可以按如下方式重新表述:

newtype Reader r a = Reader (r -> a)

This binds one name in the type namespace and one name in the value namespace, but the confusing part is that they're both named Reader ! 这会绑定类型命名空间中的一个名称和值命名空间中的一个名称,但令人困惑的部分是它们都被命名为Reader This is totally allowed in Haskell, though, since the namespaces are separate. 但是,在Haskell中完全允许这样做,因为名称空间是分开的。 Each Reader has a kind/type in this case, too: 在这种情况下,每个Reader都有一种/类型:

Reader :: * -> * -> *
Reader :: (r -> a) -> Reader r a

Note that the type-level Reader takes two arguments, but the value-level Reader only takes one. 请注意,类型级Reader有两个参数,但值级Reader只接受一个。 When you pattern-match against a value, you are using the value-level constructor (since you're deconstructing a value that was built up using that same constructor), and the value only contains one value (as it must, since Reader is a newtype ), so the pattern only binds a single variable. 当您对值进行模式匹配时,您正在使用值级构造函数(因为您正在解构使用相同构造函数构建的值),并且该值仅包含一个值(因为它必须是,因为Reader是一个newtype ),所以模式只绑定一个变量。


What goes on in this part of the definition: (f (re)) , what is it's purpose? 在这部分定义中发生了什么: (f (re)) ,它的目的是什么?

Reader is essentially a mechanism for composing a lot of functions that all take the same argument. Reader本质上是一种组成许多函数的机制,这些函数都采用相同的参数。 It's a way to avoid having to thread a value around everywhere, since the various instances will do the plumbing automatically. 这是一种避免必须在任何地方处理值的方法,因为各种实例将自动执行管道。

To understand the definition of >>= for Reader , let's specialize the type of >>= to Reader : 要理解>>= for Reader的定义,让我们专门研究>>= to Reader的类型:

(>>=) :: Reader r a -> (a -> Reader r b) -> Reader r b

For the purpose of clarity, we can also expand Reader ra to r -> a , just to get a better intuition for what the type actually means : 为了清楚起见,我们还可以将Reader ra扩展为r -> a ,以便更好地直观了解类型的实际含义

(>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)

Let's also name the arguments here for the purpose of discussion: 为了讨论,让我们在这里命名参数:

(>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)
(>>=)    f           g             =  ...

Let's think about this for a moment. 让我们暂时考虑一下。 We are given two functions, f and g , and we are expected to produce a function that produces a value of type b from a value of type a . 我们给出了两个功能, fg ,我们预计将产生,其产生类型的值的函数b从类型的值a We only have one way to produce a b , and that's by calling g . 我们只有一种方法来产生一个b ,那就是通过调用g But in order to call g , we have to have an a , and we only have one way to get an a : call f ! 但是为了打电话给g ,我们必须有一个a ,而我们只有一种方法来获得a :call f We can call f , since it only needs an r , which we already have, so we can start attaching the functions together to produce the b we need. 我们可以调用f ,因为它只需要一个我们已经拥有的r ,所以我们可以开始将函数连接在一起以生成我们需要的b

This is a little confusing, so it might help to see this flow of values visually: 这有点令人困惑,因此可以直观地看到这种价值流:

          +------------+
          | input :: r |
          +------------+
             |       |
             v       |
+--------------+     |
| f input :: a |     |
+--------------+     |
       |             |
       v             v
  +------------------------+
  | g (f input) input :: b |
  +------------------------+

In Haskell, this looks like this: 在Haskell中,这看起来像这样:

f >>= g = \input -> g (f input) input

…or, renaming things a little to match the definition in your question: ...或者,重新命名一些东西以匹配您问题中的定义:

r >>= f = \e -> f (r e) e

Now, we need to reintroduce some wrapping and unwrapping, since the real definition is on the Reader type, not (->) directly. 现在,我们需要重新引入一些包装和解包,因为真正的定义是在Reader类型上,而不是(->)直接。 This means we need to add some uses of the Reader wrapper and the runReader unwrapper, but otherwise the definition is the same: 这意味着我们需要添加Reader包装器和runReader解包Reader一些用法,否则定义是相同的:

Reader r >>= f = Reader (\e -> runReader (f (r e)) e)

At this point, you can check your intuition: Reader is a way to thread a value around between a lot of functions, and here we're composing two functions, r and f . 在这一点上,你可以检查你的直觉: Reader是一种在很多函数之间绕过一个值的方法,在这里我们组成了两个函数rf Therefore, we should need to pass the value in twice, which we do: there are two uses of e in the above definition. 因此,我们应该需要传递的值的两倍,这是我们做的:有两个用途e上述定义。

To answer your function, Monad is class over types of kind * -> * . 为了回答你的功能, Monad是类型的 * -> *

  • [Int] :: * = list of integers [Int] :: * =整数列表
  • [] :: * -> * = list [] :: * -> * = list
  • Reader ea :: * = reader with environment e resulting in a Reader ea :: * =具有环境e的读者导致a
  • Reader e :: * -> * = reader with environment e Reader e :: * -> * =环境e的读者
  • Reader :: * -> * -> * = reader Reader :: * -> * -> * =读者

We are lousy when we say Reader has a monad instance , when we mean Reader with any environment has a monad instance . 当我们说Reader有一个monad实例时 ,我们很糟糕,当我们的意思是任何环境的Reader都有一个monad实例

(Similarly, Writer w is a Monad when w is a Monoid , Writer isn't a Monad ). (同样,当wMonoid时, Writer wMonadWriter不是Monad )。


To answer your second question, it's easier to think in terms of the Reader ea as the same as function e -> a . 要回答你的第二个问题,就Reader ea和函数e -> a更容易思考。

Function has the same monad definition, but without newtype wrappers and unwrappers. 函数具有相同的monad定义,但没有newtype包装器和unwrappers。 Think Reader = (->) : 认为Reader = (->)

instance Monad ((->) e) where
    return x = \_ -> x -- alternatively, return = const !
    r >>= f  = \e -> f (r e) e

Yet, the join definition is probably most insightful: 然而, join定义可能是最有见地的:

join :: Reader e (Reader e a) -> Reader e a

But with bare function: 但是有裸功能:

join :: (e -> e -> a) -> (e -> a)
join f e = f e e

And if we write it for Reader we have to add runReader and Reader in right places (and move variable binding to the lambda on RHS): 如果我们为Reader编写它,我们必须在正确的位置添加runReaderReader (并将变量绑定移动到RHS上的lambda):

join f = Reader $ \e -> runReader (runReader f e) e
  1. There's no partial application. 没有部分申请。 Reader is a newtype definition that 'wraps' exactly one value ( runReader ), which is a function of the type e -> a . Reader是一个newtype定义,它''包含' 一个值( runReader ),它是e -> a类型的函数。 So, Reader r just pattern-matches the function out of the Reader wrapper. 因此, Reader r只是模式匹配Reader包装器中的功能。 r is bound to the function of type e -> a . r绑定到e -> a类型的函数。

  2. f is a function, as is r . f是一个函数,就像r re invokes the function r with the value e , and then f is invoked with the result of that function call. re使用值e调用函数r ,然后使用该函数调用的结果调用f

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM