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

问题描述 投票:8回答:1

我坚持在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函数的定义,我们将描述您需要做什么并编写代码。如果您打开上面适用的Reader类型,您将获得以下内容。

<*> :: (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。但我仍然无法看到如何构造一个lambda表达式,它接受r并返回b类型的东西,给定rabra

haskell applicative
1个回答
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 combinator(和pureK)。

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