Haskell:了解应用函子的纯函数

Haskell: Understanding the pure function for Applicative functors

我正在学习 Applicative Functors,纯函数具有以下类型声明:

pure :: a -> f a 

我知道纯函数接受任何类型的值,returns 一个包含该值的应用值。因此,如果应用实例是 Maybepure 3 将给出 Just 3.

但是,当您将 pure 应用于已在应用值内的值时会发生什么?例如。如果您执行 pure Just 3?

之类的操作会怎样

What happens when you apply pure to a value that's already inside an applicative value?

它只是被包裹在一个额外的层中。最终结果是一个应用值嵌套在另一个应用值中。

E.g. what happens if you do something like pure Just 3?

这是一个有趣的问题,尽管可能不是出于您所指的原因。这里的重点是区分 pure (Just 3)——这可能是你的意思——和 pure Just 3 = (pure Just) 3,这是你写的。这给出了两种情况:

  • pure (Just 3) 只是将 pure 应用于值 Just 3,正如我上面所讨论的那样,它给出了 Just (Just 3),这是一个嵌套的应用值。
  • (pure Just) 3 是一个有趣的案例。回想一下 pureJust 的类型:

    pure :: Applicative f => a -> f a
    Just :: a -> Maybe a
    -- so:
    pure Just :: Applicative f => f (a -> Maybe a)
    

    换句话说,pure Just 获取函数 Just 并将其包装在一个应用值中。

    接下来,我们要获取 pure Just 并将其应用于值 3。但是我们不能这样做,因为 f (a -> Maybe a) 是一个值,而不是一个函数!所以 (pure Just) 3 应该会导致类型错误。

    …除了结果是类型检查就好了!所以我们错过了一些东西。在这种情况下,事实证明有一个适用于函数的实例:

    instance Applicative ((->) r) where
      pure x = \r -> x
      (<*>) = _irrelevant_here
    

    语法有点滑稽,但它基本上意味着 r -> ... 是一个应用程序。这个特殊的实例被称为 Reader monad,它的使用非常广泛。 (有关此特定数据类型的更多信息,请参阅 here or here。)这个想法是 r -> a 可以在给定 r 输入的情况下计算 a;在这种情况下,pure x 创建一个忽略其输入的函数,并且 returns x 总是,并且 f <*> xr 输入提供给两个 fx,然后将两者结合起来。在这种情况下,我们只使用 pure,因此很容易手动计算 (pure Just) 3

    (pure Just) 3
    = (\r -> Just) 3
    = Just
    

    所以这里的想法是 pureJust 包装在一个应用值中,在这种情况下恰好是一个函数;然后,我们将此函数应用于 3,它摆脱了包装以显示原始 Just.

首先,pure 具有以下类型:

pure :: Applicative f => a -> f a

为了简单起见,想想那种f

:k f 
f :: * -> *

a的种类是*

那么 a 的类型就是 a,任何 a,是所有类型中最具多态性的(但记住种类 *)。所以你并不关心a的值,你只是有一个限制,那就是typeclass Applicative,还有f的那种(记住* -> *

所以在这种情况下:

gchi> pure 3 :: Maybe Int
ghci> Just 3

这里 fMaybea3

同理

gchi> pure $ Just 3 :: Maybe (Maybe Int)
gchi> Just (Just 3)

这里 f 又是 Maybea 又是 Just 3

你可以玩一点把类型改成纯的:

gchi> pure 3 :: [Double]
ghci> [3.0]

这里,f是[],a3

同理

ghci> pure [3] :: [[Double]]
ghci> [[3.0]]

终于到了,f又是[]a是[3]