如何使 Applicative 的实例成为某种数据类型

How to make instance of Applicative a certain data type

我正在阅读关于 Haskell 的 Graham Hutton 的书,不知道如何在练习的一部分中进行。习题如下:

给定以下类型表达式

data Expr a = Var a | Val Int | Add (Expr a) (Expr a) deriving Show

包含某种类型 a 的变量,展示如何将这种类型变成 Functor、Applicative 和 Monad 的实例 类。借助示例,解释此类型的 >>= 运算符的作用。


我在定义 Applicative 的 <*> 运算符时遇到了问题。 <*> 的类型是:

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

我不明白 (Val n) <*> mx 是如何工作的,因为理论上我需要提供一个 Expr b,但我只有一个 Expr a 并且没有转换功能 (a -> b).

我也不明白在(Add l r) <*> mx情况下该怎么做。


这是我的实现。

instance Functor Expr where
    --fmap :: (a -> b) -> Expr a -> Expr b
    fmap g (Var x) = Var (g x)
    fmap g (Val n) = Val n
    fmap g (Add l r) = Add (fmap g l) (fmap g r)


instance Applicative Expr where
    --pure :: a -> Expr a
    pure = Var

    -- <*> :: Expr (a -> b) -> Expr a -> Expr b
    (Var g) <*> mx = fmap g mx
    --(Val n) <*> mx = ???
    --(Add l r) <*> mx = ???

instance Monad Expr where
    -- (>>=) :: Expr a -> (a -> Expr b) -> Expr b
    (Var x) >>= g = g x
    (Val n) >>= g = Val n
    (Add l r) >>= g = Add (l >>= g) (r >>= g)


expr = Add (Add (Var 'a') (Val 4)) (Var 'b')

最后,我对monad中的>>=有疑问。这个操作符的思想是做一些代入变量之类的事情?喜欢:

expr >>= (\x -> if x == 'a' then Val 6 else Var x) >>= (\x -> if x == 'b' then Val 7 else Var x)

当您定义了 pure(>>=) 时,(<*>) 的一种可能定义是

(<*>) = Control.Monad.ap

其中 ap 在标准库中定义为

ap :: Monad m => m (a -> b) -> m a -> m b
ap mf mx = do
  f <- mf
  x <- mx
  pure (f x)

事实上 (<*>) 的任何定义都必须等同于 Monad 实例。

正如您正确注意到的那样,在这种情况下:

(Val n) <*> mx = ???

你有:

Val n :: Expr (a -> b)
mx :: Expr a

你需要制作一个Expr b。你还记得这个案例吗:

fmap g (Val n) = ???

当你有:

g :: a -> b
Val n :: Expr a

你需要制作一个 Expr b?您在那里找到了解决方案。

案例:

(Add l r) <*> mx

你有:

l :: Expr (a -> b)
r :: Expr (a -> b)
mx :: Expr a

你需要制作一个Expr b。如果你有一些函数可以接受 lmx 并创建一个 Expr b。这样的函数,如果存在的话,可能会有签名:

someFunc :: Expr (a -> b) -> Expr a -> Expr b

当然,someFunc l mxsomeFunc r mx都是Expr b类型,只用一个就可惜了。如果有某种方法可以从两个 Expr b 部分 构建 一个 Expr b,那真的是蜜蜂的膝盖。

您对 Val n 案例中可用的类型有轻微的误解。您没有 Expr a,而是 Expr (a -> b),根本没有 ab(甚至 a -> b 中的函数也没有,因为 Val 只包含一个 Int)。事实上,这种情况很简单 因为 你周围没有有用的值:你可能做的唯一合理的事情就是使用构造函数 Val 产生输出,因为你有无法凭空制造 bVal 的类型可以专门化为 Val :: Int -> Expr b,幸运的是,你有一个 Int,所以你可以写:

(Val n) <*> mx = Val n