如何写< *>在为Reader r编写Applicative实例时

时间:2016-10-15 14:20:59

标签: haskell applicative

我在Haskell Book,“第22章读者”中练习。练习说“实施读者的应用”,它给出了以下内容:

{-# LANGUAGE InstanceSigs #-}

newtype Reader r a =
  Reader { runReader :: r -> a }

instance Applicative (Reader r) where 
  pure :: a -> Reader r a
  pure a = Reader $ ???

  (<*>) :: Reader r (a -> b) -> Reader r a -> Reader r b
  (Reader rab) <*> (Reader ra) = Reader $ \r -> ???

我在编写pure实例之后能够编写Functor(我编写了Functor实例,因为否则GHC抱怨“(Functor (Reader r)) …没有出现任何实例来自实例声明的超类在‘Applicative (Reader r)’“的实例声明中:

{-# LANGUAGE InstanceSigs #-}

newtype Reader r a =
  Reader { runReader :: r -> a }

instance Functor (Reader r) where
  fmap f (Reader x) = Reader (f . x)

instance Applicative (Reader r) where
  pure :: a -> Reader r a
  pure a = Reader $ \_ -> a

  (<*>) :: Reader r (a -> b) -> Reader r a -> Reader r b
  (Reader rab) <*> (Reader ra) = Reader $ \r -> ???

但我坚持使用???部分。

本书给出了以下提示:

  

我们已经为您启动了apply函数的定义   描述你需要做什么,然后编写代码。如果你打开包装   读者的类型适用于上面,你得到以下。

<*> :: (r -> a -> b) 
    -> (r -> a) 
    -> (r -> b)

 -- contrast this with the type of fmap

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

那有什么区别?不同之处在于apply   fmap,也会使用r类型的参数。

     

实现目标。

是的,但如何做到这一点?使用类型的孔,编译器告诉我???的类型必须是b。但我仍然无法看到如何构造一个带有r的lambda表达式,并返回类型为b的东西,给定rabra

1 个答案:

答案 0 :(得分:11)

让我们打网球。看看你在范围内的作品,

rab :: r -> (a -> b)
ra :: r -> a
r :: r

以及b的目标类型,您可以看到,b的唯一方法是将rab应用于两个参数。

Reader rab <*> Reader ra = Reader $ \r -> rab _ _

现在,第一个洞的类型为r,你只有一个r

Reader rab <*> Reader ra = Reader $ \r -> rab r _

剩下的洞的类型为a。您在范围内唯一的ara的返回值,

Reader rab <*> Reader ra = Reader $ \r -> rab r (ra _)

ra的参数必须是r,再次只有一个选择。

Reader rab <*> Reader ra = Reader $ \r -> rab r (ra r)

请注意rabra都接收r作为参数。组合Reader计算中的所有步骤都可以访问相同的环境。

顺便提一下,此定义使<*>等同于着名的S combinatorpureK)。