我正在阅读Graham Hutton 的《Programming in Haskell》一书。我有一个关于练习 12.7 的问题。任务是展示如何将类型
data Expr a = Var a | Val Int | Add (Expr a) (Expr a)
变成 Functor 和 Applicative 类的实例。
将其变成 Functor 的实例很简单:
instance Functor Expr where
--fmap :: (a -> b) -> Expr a -> Expr b
fmap f (Var x) = Var $ f x
fmap f (Val i) = Val i
fmap f (Add e e') = Add (fmap f e) (fmap f e')
但是,我不确定如何实现该<*>
运算符,特别是当它的一个或两个输入都有构造函数 Val 时,我不清楚如何定义它:
instance Applicative Expr where
--pure :: a -> Expr a
pure x = Var x
--(<*>) :: Expr (a -> b) -> Expr a -> Expr b
(Var f) <*> (Var x) = Var (f x)
(Var f) <*> (Add e1 e2) = Add (fmap f e1) (fmap f e2)
(Add fe1 fe2) <*> e = Add (fe1 <*> e) (fe2 <*> e)
_ <*> _ = ???
我的问题是:我怎样才能完成我的实施<*>
以使应用定律成立?
我只是给你一个提示。
要定义
t1 <*> t2
,请扫描表达式树t1
并找到存在变量的位置,即Var f
某些函数。用新的表达式树f
替换 中的每个变量。保留 中的非变量。t1
fmap f t2
t1
您不需要扫描
t2
您的<*>
(除非隐式地通过fmap
)。请注意,这尊重类型。我们有
t1 :: Expr (a -> b)
,因此f :: a -> b
。此外,,t2 :: Expr a
因此正如fmap f t2 :: Expr b
所希望的那样。非正式示例:
一旦完成定义,您就可以尝试证明应用定律。
最后要说的是,在我看来,在这种情况下,
Expr
首先将其视为 monad 更容易。这是因为我发现join :: Expr (Expr a) -> Expr a
定义 比 更自然<*>
。一旦你理解了 ,join
获得 就相对容易得多<*>
,至少对我来说是这样。