我正在学习Applicative Functors,纯函数具有以下类型声明:
pure :: a -> f a
我知道pure函数可以接受任何类型的值,并返回其中包含该值的适用值。因此,如果应用实例为Maybe
,则pure 3
将给出Just 3
。
但是,当您将纯值应用到应用值中已经包含的值时会发生什么?例如。如果您进行类似pure Just 3
的操作会怎样?
答案 0 :(得分:9)
当您将纯值应用于应用值中的值时会发生什么?
它只是包裹在一个附加层中。最终结果是一个嵌套在另一个应用程序值内的应用程序值。
例如如果您进行类似
pure Just 3
的操作会怎样?
这是一个有趣的问题,尽管可能不是出于您的意思。这里的重点是要区分pure (Just 3)
(这可能是您的意思)与pure Just 3 = (pure Just) 3
(即您编写的内容)。这给出了两种情况:
pure (Just 3)
只需将pure
应用于值Just 3
,正如我上面所讨论的,得出Just (Just 3)
,这是一个嵌套的应用值。 (pure Just) 3
是一个有趣的情况。回忆一下pure
和Just
的类型:
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或here。)想法是,r -> a
可以在给定a
输入的情况下计算r
;在这种情况下,pure x
创建一个忽略其输入并始终返回x
的函数,并且f <*> x
将r
的输入同时馈送到f
和{{1 }},然后将两者结合起来。在这种情况下,我们仅使用x
,因此可以很容易地手动评估pure
:
(pure Just) 3
因此,这里的想法是(pure Just) 3
= (\r -> Just) 3
= Just
将pure
包装为一个应用值,在这种情况下,该值恰好是一个函数;然后,我们将此功能应用于Just
,它会摆脱包装器以显示原始3
。
答案 1 :(得分:4)
首先,pure具有以下类型:
pure :: Applicative f => a -> f a
为简化起见,请考虑f的种类
:k f
f :: * -> *
a
的种类为*
然后,a
的类型就是a
,任何a
,都是所有类型中最多态的(但要记住种类*
)。因此,您实际上并不关心a的值,只是有一个限制,那就是typeclass
适用性和f
的种类(记住* -> *
)
在这种情况下:
gchi> pure 3 :: Maybe Int
ghci> Just 3
这里f
是Maybe
,而a
是3
以同样的方式
gchi> pure $ Just 3 :: Maybe (Maybe Int)
gchi> Just (Just 3)
这里f
再次为Maybe
,a
为Just 3
您可以进行一些操作,将类型更改为纯:
gchi> pure 3 :: [Double]
ghci> [3.0]
这里,f
是[],a
是3
相同的方式
ghci> pure [3] :: [[Double]]
ghci> [[3.0]]
最后,f
再次是[]
,而a
是[3]