39

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

对我来说,这有点太抽象了。好的,我已经看到了 Arrow 实例,在(->)我看来,它(->)可以用在实例表示法中,但不能用在类型声明中(这对于另一个问题来说是单独的东西)。

有没有人使用(->)Monad 的例子?还是一个好的链接?

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

4

2 回答 2

36

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

可是等等!这正是单子的意义所在!

因此,我们可以通过将 传递给和来(->) r为该实现创建一个 Monad 实例。这就是 Monad 实例的作用。f >>= grfg(->) r

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

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

这种将环境传递给计算然后可以在本地查询和修改它的模式不仅对(->) rmonad 有用,这就是为什么它被抽象到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的 monad 相同,除了它在其他一些转换后的 monad 中提供计算。(->) r

于 2011-03-15T10:39:34.640 回答
28

要为 定义一个 monad (->) r,我们需要两个操作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

r使用这个 monad 就像向每个函数传递一个额外的参数。您可以将其用于配置,或者将选项传递到程序的深处。

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

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)手写Monoida的实例,给定一个 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

将 Applicative 实例用于(->) r或与

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

将 Monad 实例用于(->) r.

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

于 2011-03-15T19:07:36.380 回答