如何使用( - >)Monad实例和关于( - >)的混淆

时间:2011-03-15 10:09:38

标签: haskell functional-programming monads pointfree

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

至于我,这有点过于抽象。好的,我在(->)上看到了箭头实例,在我看来,(->)可以在实例表示法中使用,但不能在类型声明中使用(这只是另一个问题的东西)。

有没有使用(->)作为Monad实例的示例?还是一个很好的链接?

很抱歉,如果此处已经讨论过这个问题,但是搜索(->) Monad实例”会给你很多点击,因为你可以想象......因为几乎所有的问题都是关于Haskell某处涉及(->)或“Monad”。

2 个答案:

答案 0 :(得分:31)

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

但是等等!这正是monads的意思!

因此,我们可以通过将(->) r传递给f >>= gr来为f创建实现g的Monad实例。这就是(->) 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,它只是newtype的{​​{1}}包装,所以它是一样的作为我在这里描述的r -> m a monad的东西,除了它在其他一些变形的monad中提供计算。

答案 1 :(得分:26)

要为(->) 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。您可以使用它进行配置,或者将选项深入到程序的内容中。

我们可以通过验证三个monad法则来检查它是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

最终的monad法律:

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

有很多类涉及( - &gt;)的实例,例如,你可以在{{1}上给出一个Monoid,为(b - &gt; a)手写Monoid的实例as:

a

但是给定Monad / Applicative实例,您还可以使用

定义此实例
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

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

的Applicative实例
(->) r

使用Monad实例instance Monoid a => Monoid (r -> a) where mempty = return mempty mappend = liftM2 mappend

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