如何使用 Monad 的 (->) 实例以及关于 (->) 的困惑

发布于 2024-10-22 18:03:59 字数 411 浏览 6 评论 0原文

在不同的问题上,我在评论中找到了有关使用 Monad 的 (->) 实例的提示,例如用于实现无点样式。

对于我来说,这有点太抽象了。好的,我在 (->) 上看到了 Arrow 实例,在我看来, (->) 可以在实例符号中使用,但不能在类型中使用声明(这本身就是另一个问题的内容)。

有没有人使用 (->) 作为 Monad 实例的例子?或者一个好的链接?

抱歉,如果这个问题可能已经在这里讨论过,但是搜索(->) Monad实例”会给你带来很多你可以想象的命中......因为几乎每个关于 Haskell 的问题都涉及 (->) 或“Monad”。

At different questions I've found hints in comments concerning using the (->) instance of Monads e.g. for realizing point-free style.

As for me, this is a little too abstract. Ok, I've seen Arrow instances on (->) and it seems to me, that (->) can be used in instance notations but not in type declarations (that would alone be stuff for another question).

Has anyone examples using (->) as instance of Monad? Or a good link?

Sorry if this question may already have been discussed here, but searching for "(->) Monad instance" gives you many many hits as you can imagine ... since nearly every question about Haskell somewhere involves (->) or "Monad".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

想你的星星会说话 2024-10-29 18:03:59

对于给定类型r,类型r -> 的函数a 可以被认为是使用类型为 r 的环境提供 a 的计算。给定两个函数 r -> aa -> (r -> b),很容易想象,当给定一个环境(同样,类型为 r)时,人们可以组合这些。

但是等等!这正是 monad 的意义所在!

因此,我们可以为 (->) r (*) 创建一个 Monad 实例,通过传递 r 来实现 f >>= g code> 为 fg。这就是 (->) r 的 Monad 实例所做的事情。

要实际访问环境,您可以使用 id :: r -> r,您现在可以将其视为在环境 r 中运行并提供 r 的计算。要创建本地子环境,您可以使用以下方法:

inLocalEnvironment :: (r -> r) -> (r -> a) -> (r -> a)
inLocalEnvironment xform f = \env -> f (xform env)

这种将环境传递给计算的模式,然后可以在本地查询并修改它,这不仅适用于 (->) r monad,这就是为什么它被抽象为 MonadReader 类,使用比我在这里使用的更明智的名称:

http://hackage.haskell.org/packages/archive/mtl/2.0.1.0/doc /html/Control-Monad-Reader-Class.html

基本上,它有两个实例:我们在这里看到的 (->) rReaderT r m< /code>,它只是一个 r -> 的 newtype 包装器。 m a,所以它与我在这里描述的 (->) r monad 是一样的,只不过它在其他一些转换后的 monad 中提供计算。

(*) (->) r 乍一看可能很有趣,但这是 Haskell 语法,用于部分地将两参数类型构造函数 (->) 应用到第一个类型参数 r,即它的含义类似于 r -> _ 第二个参数尚未给出。因此 (->) r a (或 ((->) r) a)与 r ->;一个。

For a given type r, the function of type r -> a can be thought of as a computation delivering an a using an environment typed r. Given two functions r -> a and a -> (r -> b), it's easy to imagine that one can compose these when given an environment (again, of type r).

But wait! That's exactly what monads are about!

So we can create an instance of Monad for (->) r (*) that implements f >>= g by passing the r to both f and g. This is what the Monad instance for (->) r does.

To actually access the environment, you can use id :: r -> r, which you can now think of as a computation running in an environment r and delivering an r. To create local sub-environments, you can use the following:

inLocalEnvironment :: (r -> r) -> (r -> a) -> (r -> a)
inLocalEnvironment xform f = \env -> f (xform env)

This pattern of having an environment passed to computations that can then query it and modify it locally is useful for not just the (->) r monad, which is why it is abstracted into the MonadReader class, using much more sensible names than what I've used here:

http://hackage.haskell.org/packages/archive/mtl/2.0.1.0/doc/html/Control-Monad-Reader-Class.html

Basically, it has two instances: (->) r that we've seen here, and ReaderT r m, which is just a newtype wrapper around r -> m a, so it's the same thing as the (->) r monad I've described here, except it delivers computations in some other, transformed monad.

(*) (->) r might look funny at first, but this is Haskell syntax for partially applying the two-parameter type constructor (->) to the first type argument r, i.e. it means something like r -> _ with the second argument not yet given. So (->) r a (or ((->) r) a) is the same as r -> a.

放我走吧 2024-10-29 18:03:59

要为 (->) r 定义一个 monad,我们需要两个操作,return(>>=),具体取决于三个定律:

instance Monad ((->) r) where

如果我们查看 (->) r 的 return 签名,

    return :: a -> r -> a

我们可以看到它只是一个常量函数,它忽略了它的第二个参数。

    return a r = a

或者,

    return = const

要构建 (>>=),如果我们使用 monad (->) r 专门化其类型签名,

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

实际上只有一个可能的定义。

    (>>=) x y z = y (x z) z

使用这个 monad 就像向每个函数传递一个额外的参数 r 。您可以使用它进行配置,或者将选项深入到程序的内部。

我们可以通过验证三个单子定律来检查它是否是一个单子:

1. return a >>= f = f a 

return a >>= f 
= (\b -> a) >>= f -- by definition of return
= (\x y z -> y (x z) z) (\b -> a) f -- by definition of (>>=)
= (\y z -> y ((\b -> a) z) z) f -- beta reduction
= (\z -> f ((\b -> a) z) z) -- beta reduction
= (\z -> f a z) -- beta reduction
= f a -- eta reduction

2. m >>= return = m

m >>= return
= (\x y z -> y (x z) z) m return -- definition of (>>=)
= (\y z -> y (m z) z) return -- beta reduction
= (\z -> return (m z) z) -- beta reduction
= (\z -> const (m z) z) -- definition of return
= (\z -> m z) -- definition of const
= m -- eta reduction

最终的单子定律:

3. (m >>= f) >>= g  ≡  m >>= (\x -> f x >>= g)

遵循类似、简单的等式推理。

我们还可以为 ((->) r) 定义许多其他类,例如 Functor,

instance Functor ((->) r) where

如果我们查看 的签名,

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

我们可以看到它只是组合!

   fmap = (.)

类似地,我们可以创建一个 Applicative 实例

instance Applicative ((->) r) where
   -- pure :: a -> r -> a
   pure = const

   -- (<*>) :: (r -> a -> b) -> (r -> a) -> r -> b
   (<*>) g f r = g r (f r)

拥有这些实例的好处是它们可以让您使用所有 MonadApplicative 操作函数时的组合器。

有很多涉及 (->) 的类实例,例如,您可以手写 Monoid for (b -> a),给定 a 上的 Monoid 为:

enter code here
instance Monoid a => Monoid (b -> a) where
    -- mempty :: Monoid a => b -> a
    mempty _ = mempty
    -- mappend :: Monoid a => (b -> a) -> (b -> a) -> b -> a
    mappend f g b = f b `mappend` g b

但给定 Monad/Applicative例如,您还可以

instance Monoid a => Monoid (r -> a) where
    mempty = pure mempty
    mappend = liftA2 mappend

使用 (->) r 的 Applicative 实例或

instance Monoid a => Monoid (r -> a) where
    mempty = return mempty
    mappend = liftM2 mappend

使用 (->) r 的 Monad 实例来定义此实例。

这里节省的空间很小,但是,例如 #haskell IRC 频道上的 lambdabot 提供的用于生成无点代码的 @pl 工具就滥用了这些实例。

To define a monad for (->) r, we need two operations, return and (>>=), subject to three laws:

instance Monad ((->) r) where

If we look at the signature of return for (->) r

    return :: a -> r -> a

we can see its just the constant function, which ignores its second argument.

    return a r = a

Or alternately,

    return = const

To build (>>=), if we specialize its type signature with the monad (->) r,

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

there is really only one possible definition.

    (>>=) x y z = y (x z) z

Using this monad is like passing along an extra argument r to every function. You might use this for configuration, or to pass options way down deep into the bowels of your program.

We can check that it is a monad, by verifying the three monad laws:

1. return a >>= f = f a 

return a >>= f 
= (\b -> a) >>= f -- by definition of return
= (\x y z -> y (x z) z) (\b -> a) f -- by definition of (>>=)
= (\y z -> y ((\b -> a) z) z) f -- beta reduction
= (\z -> f ((\b -> a) z) z) -- beta reduction
= (\z -> f a z) -- beta reduction
= f a -- eta reduction

2. m >>= return = m

m >>= return
= (\x y z -> y (x z) z) m return -- definition of (>>=)
= (\y z -> y (m z) z) return -- beta reduction
= (\z -> return (m z) z) -- beta reduction
= (\z -> const (m z) z) -- definition of return
= (\z -> m z) -- definition of const
= m -- eta reduction

The final monad law:

3. (m >>= f) >>= g  ≡  m >>= (\x -> f x >>= g)

follows by similar, easy equational reasoning.

We can define a number of other classes for ((->) r) as well, such as Functor,

instance Functor ((->) r) where

and if we look at the signature of

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

we can see that its just composition!

   fmap = (.)

Similarly we can make an instance of Applicative

instance Applicative ((->) r) where
   -- pure :: a -> r -> a
   pure = const

   -- (<*>) :: (r -> a -> b) -> (r -> a) -> r -> b
   (<*>) g f r = g r (f r)

What is nice about having these instances is they let you employ all of the Monad and Applicative combinators when manipulating functions.

There are plenty of instances of classes involving (->), for instance, you could hand-write the instance of Monoid for (b -> a), given a Monoid on a as:

enter code here
instance Monoid a => Monoid (b -> a) where
    -- mempty :: Monoid a => b -> a
    mempty _ = mempty
    -- mappend :: Monoid a => (b -> a) -> (b -> a) -> b -> a
    mappend f g b = f b `mappend` g b

but given the Monad/Applicative instance, you can also define this instance with

instance Monoid a => Monoid (r -> a) where
    mempty = pure mempty
    mappend = liftA2 mappend

using the Applicative instance for (->) r or with

instance Monoid a => Monoid (r -> a) where
    mempty = return mempty
    mappend = liftM2 mappend

using the Monad instance for (->) r.

Here the savings are minimal, but, for instance the @pl tool for generating point-free code, which is provided by lambdabot on the #haskell IRC channel abuses these instances quite a bit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文