为了为
(->) r
定义一个单子,我们需要两个操作,
return
和
(>>=)
,并满足三个定律:
instance Monad ((->) r) where
如果我们看一下 (->) r
的返回签名
return :: a -> r -> a
我们可以看到这只是一个常数函数,它忽略了它的第二个参数。
return a r = a
或者,另外一种选择是,
return = const
要构建(>>=)
,如果我们使用单子(->) 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
最终的单子法则:
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 = const
(<*>) g f r = g r (f r)
拥有这些实例的好处在于,它们让您在操作函数时可以使用所有
Monad和
Applicative组合器。
涉及(->)的类有很多实例,例如,如果给定
a
上的Monoid,则可以手动编写(b -> a)的
Monoid实例,如下所示:
enter code here
instance Monoid a => Monoid (b -> a) where
mempty _ = mempty
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
的单子实例。
在这里节省的不多,但是,例如生成无点代码的 @pl 工具,由 #haskell IRC 频道上的 lambdabot 提供,相当滥用了这些实例。
type F a b = (->) a b
和f :: (->) a b
。 - R. Martinho Fernandes(->) a b
理解为a->b
,对吗?所以,(->) 是类型中的一个运算符。 - makelcreturn :: a -> (a -> r)
的问题! - yatima2975