首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么应用函数式函数可以有副作用,而函数式函数不能?

为什么应用函数式函数可以有副作用,而函数式函数不能?
EN

Stack Overflow用户
提问于 2013-01-29 13:56:21
回答 4查看 2.2K关注 0票数 17

我觉得问这个问题很傻,但这个问题已经在我的脑海中徘徊了一段时间,我找不到任何答案。

所以问题是:为什么应用函数器可以有副作用,而函数器不能?

也许他们可以,但我从来没有注意到...?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-01-29 18:15:41

这个答案有点过于简单,但如果我们将副作用定义为受以前的计算影响的计算,很容易看出Functor类型类不足以处理副作用,因为没有办法链接多个计算。

代码语言:javascript
复制
class Functor f where
    fmap :: (a -> b) -> f a -> f b

函数器唯一能做的事情就是通过一些纯函数a -> b来改变计算的最终结果。

但是,应用函数器添加了两个新函数:pure<*>

代码语言:javascript
复制
class Functor f => Applicative f where
    pure   :: a -> f a
    (<*>)  :: f (a -> b) -> f a -> f b

<*>是这里的关键区别,因为它允许我们链接两个计算:f (a -> b) (产生函数的计算)和f a (提供函数所应用的参数的计算)。使用pure<*>可以定义例如

代码语言:javascript
复制
(*>) :: f a -> f b -> f b

它简单地链接了两次计算,丢弃了第一次计算的最终结果(但可能会应用“副作用”)。

因此,简而言之,它是链式计算的能力,这是对计算中可变状态等效果的最低要求。

票数 25
EN

Stack Overflow用户

发布于 2013-01-29 18:57:04

Functor没有效果的说法是不正确的。每个Applicative (以及通过WrappedMonad的每个Monad )都是一个Functor。主要的区别是ApplicativeMonad提供了如何使用这些效果,如何组合它们的工具。粗略地

  • Applicative允许您对效果进行排序和组合值。此外,inside.
  • Monad还允许您根据前一个效果的结果确定下一个效果。

然而,Functor只允许你修改里面的值,它没有提供工具来做任何与效果相关的事情。因此,如果某些东西只是Functor而不是Applicative,并不意味着它没有效果。它只是没有一个机制如何以这种方式将它们组合在一起。

更新:以为例,请考虑

代码语言:javascript
复制
import Control.Applicative

newtype MyF r a = MyF (IO (r, a))

instance Functor (MyF r) where
    fmap f (MyF x) = MyF $ fmap (fmap f) x

这显然是一个带有效果的Functor实例。只是我们没有办法定义符合Applicative的具有这些效果的操作。除非我们对r施加一些额外的约束,否则无法定义Applicative实例。

票数 31
EN

Stack Overflow用户

发布于 2013-01-30 09:43:50

这里的其他答案正确地表明,函数器不允许副作用,因为它们不能组合或排序,这在很大程度上是正确的,但有一种方法可以对函数器进行排序:向内排序。

让我们编写一个有限的Writer函数器。

代码语言:javascript
复制
data Color    = R    | G    | B
data ColorW a = Re a | Gr a | Bl a deriving (Functor)

然后将Free monad类型应用于它

代码语言:javascript
复制
data Free f a = Pure a | Free (f (Free f a))

liftF :: Functor f => f a -> Free f a
liftF = Free . fmap Pure

type ColorWriter = Free ColorW

red, blue, green :: a -> ColorWriter a
red   = liftF . Re
green = liftF . Gr
blue  = liftF . Bl

当然,通过自由属性,这形成了一个单体,但效果实际上来自函数器的“层”。

代码语言:javascript
复制
interpretColors :: ColorWriter a -> ([Color], a)
interpretColors (Pure a) = ([], a)
interpretColors (Free (Re next)) = let (colors, a) = interpretColors next
                                   in (R : colors, a)
...

所以,这是一种技巧。实际上,“计算”是由自由单体引入的,但计算的材料,隐藏的上下文,只是由一个函数器引入的。事实证明,您可以对任何数据类型执行此操作,它甚至不需要是Functor,但Functor提供了一种清晰的方法来构建它。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14576543

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档