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

问题描述 投票:0回答:2

在不同的问题上,我在评论中找到了有关使用 Monad 的

(->)
实例的提示,例如实现无点风格。

对于我来说,这有点太抽象了。好吧,我在

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

有没有人使用

(->)
作为 Monad 实例的例子?或者有好的链接吗?

很抱歉,如果这个问题可能已经在这里讨论过,但是搜索

(->)
Monad 实例” 会给你带来很多你可以想象的结果......因为几乎每个关于 Haskell 的问题都涉及
(->)
或“Monad” ”.

haskell functional-programming monads pointfree
2个回答
38
投票

对于给定类型

r
,类型
r -> a
的函数可以被认为是使用类型为
a
的环境传递
r
的计算。给定两个函数
r -> a
a -> (r -> b)
,很容易想象在给定环境(同样,类型为
r
)时可以组合这些函数。

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

因此,我们可以为

(->) r
创建一个 Monad 实例(这是 Haskell 语法,用于将双参数类型构造函数
(->)
部分应用到第一个类型参数
r
,即,它的含义与第二个类型参数
r -> _
类似)参数尚未给出),通过将
f >>= g
传递给
r
f
来实现
g
。这就是
(->) 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

基本上,它有两个实例:我们在这里看到的

(->) r
ReaderT r m
,它只是
newtype
r -> m a
包装器,所以它与我之前的
(->) r
monad 是一样的此处描述,但它在其他一些经过转换的 monad 中提供计算。


29
投票

要为

(->) r
定义一个 monad,我们需要两个操作,
return
(>>=)
,并遵守三个定律:

instance Monad ((->) r) where

如果我们看一下退货的签名

(->) r

    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 组合器。

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

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
或 with

的 Applicative 实例
instance Monoid a => Monoid (r -> a) where
    mempty = return mempty
    mappend = liftM2 mappend

使用 Monad 实例来实现

(->) r

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

© www.soinside.com 2019 - 2024. All rights reserved.