Applicative IO 是基于 Monad IO 的函数实现的吗?

发布于 2024-11-18 01:28:32 字数 699 浏览 5 评论 0原文

在“为大善而学 Haskell!”作者声称 Applicative IO 实例是这样实现的:

instance Applicative IO where
    pure = return
    a <*> b = do
        f <- a
        x <- b
        return (f x)

我可能是错的,但似乎 returndo 特定的构造(一些糖化绑定 (>>=) ) 来自 Monad IO。假设这是正确的,我的实际问题是:

为什么Applicative IO实现依赖于Monad IO函数/组合器?

不是Applicative< /code> 不如Monad强大的概念?


编辑(一些说明):

这种实现违背了我的直觉,因为根据 Typeclassopedia 文章,给定类型需要在它之前Applicative可以制成Monad(或者理论上应该如此)。

In "Learn You a Haskell for Great Good!" author claims that Applicative IO instance is implemented like this:

instance Applicative IO where
    pure = return
    a <*> b = do
        f <- a
        x <- b
        return (f x)

I might be wrong, but it seems that both return, and do-specific constructs (some sugared binds (>>=) ) comes from Monad IO. Assuming that's correct, my actual question is:

Why Applicative IO implementation depends on Monad IO functions/combinators?

Isn't Applicative less powerfull concept than Monad?


Edit (some clarifications):

This implementation is against my intuition, because according to Typeclassopedia article it's required for a given type to be Applicative before it can be made Monad (or it should be in theory).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

家住魔仙堡 2024-11-25 01:28:32

(...) 根据 Typeclassopedia 文章,给定类型必须先成为 Applicative,然后才能成为 Monad(或者理论上应该如此)。

是的,除了你的括号之外,这正是这里的问题。理论上,任何 Monad 应该 也是一个 Applicative,但由于历史原因(即,因为 Monad 已经存在了更长的时间)。这也不是 Monad 的唯一特性。

考虑相关类型类的实际定义,取自 Hackage 上的 base 包的源代码。

这是Applicative

class Functor f => Applicative f where
    pure  :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b
    (*>)  :: f a -> f b -> f b
    (<*)  :: f a -> f b -> f a

...关于它,我们可以观察到以下内容:

  • 鉴于当前存在的类型类,上下文是正确的,即,它需要Functor
  • 它是根据函数应用来定义的,而不是根据提升元组(从数学的角度来看可能更自然)来定义的。
  • 它包括技术上多余的运算符,相当于提升常量函数。

同时,这里是 Monad

class Monad m where
    (>>=)       :: m a -> (a -> m b) -> m b
    (>>)        :: m a -> m b -> m b
    return      :: a -> m a
    fail        :: String -> m a

...关于它,我们可以观察到以下内容:

  • 上下文不仅忽略了 Applicative,而且还忽略了 Functor,两者都忽略了。这些在逻辑上由 Monad 隐含,但没有明确要求。
  • 它也是根据函数应用来定义的,而不是使用 returnjoin 的更数学自然的定义。
  • 它包括一个技术上多余的运算符,相当于提升一个常数函数。
  • 它还包括根本不适合的 fail

一般来说,Monad 类型类与其所基于的数学概念的不同之处可以追溯到它作为编程抽象的历史。有些,比如它与 Applicative 共享的函数应用偏向,是函数式语言中存在的反映;其他的,例如失败或缺乏适当的类上下文,都是历史事故。

归根结底,拥有一个 Monad 实例意味着一个 Applicative 实例,而这又意味着一个 Functor 实例>。类上下文只是明确地形式化了这一点;无论如何,这仍然是正确的。就目前情况而言,给定一个 Monad 实例,Functor 和 Applicative 都可以以完全通用的方式定义。 ApplicativeMonad 更“强大”,但它更更通用:任何 Monad 都是如果您复制并粘贴通用实例,但存在无法定义为 MonadApplicative 实例,则会自动Applicative

类上下文,例如 Functor f =>应用性 f 说明了两件事:后者意味着前者,并且必须存在一个定义来满足该含义。在许多情况下,定义后者无论如何都会隐式定义前者,但编译器通常无法推断出这一点,因此需要显式写出两个实例。使用 EqOrd 可以观察到同样的事情 - 后者显然暗示了前者,但您仍然需要在中定义一个 Eq 实例order 为 Ord 定义一个。

(...) according to Typeclassopedia article it's required for a given type to be Applicative before it can be made Monad (or it should be in theory).

Yes, your parenthetical aside is exactly the issue here. In theory, any Monad should also be an Applicative, but this is not actually required, for historical reasons (i.e., because Monad has been around longer). This is not the only peculiarity of Monad, either.

Consider the actual definitions of the relevant type classes, taken from the base package's source on Hackage.

Here's Applicative:

class Functor f => Applicative f where
    pure  :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b
    (*>)  :: f a -> f b -> f b
    (<*)  :: f a -> f b -> f a

...about which we can observe the following:

  • The context is correct given currently existing type classes, i.e., it requires Functor.
  • It's defined in terms of function application, rather than in (possibly more natural from a mathematical standpoint) terms of lifting tuples.
  • It includes technically superfluous operators equivalent to lifting constant functions.

Meanwhile, here's Monad:

class Monad m where
    (>>=)       :: m a -> (a -> m b) -> m b
    (>>)        :: m a -> m b -> m b
    return      :: a -> m a
    fail        :: String -> m a

...about which we can observe the following:

  • The context not only ignores Applicative, but also Functor, both of which are logically implied by Monad but not explicitly required.
  • It's also defined in terms of function application, rather than the more mathematically natural definition using return and join.
  • It includes a technically superfluous operator equivalent to lifting a constant function.
  • It also includes fail which doesn't really fit in at all.

In general, the ways that the Monad type class differs from the mathematical concept it's based on can be traced back through its history as an abstraction for programming. Some, like the function application bias it shares with Applicative, are a reflection of existing in a functional language; others, like fail or the lack of an appropriate class context, are historical accidents more than anything else.

What it all comes down to is that having an instance of Monad implies an instance for Applicative, which in turn implies an instance for Functor. A class context merely formalizes this explicitly; it remains true regardless. As it stands, given a Monad instance, both Functor and Applicative can be defined in a completely generic way. Applicative is "less powerful" than Monad in exactly the same sense that it is more general: Any Monad is automatically Applicative if you copy+paste the generalized instance, but there exist Applicative instances which cannot be defined as a Monad.

A class context, like Functor f => Applicative f says two things: That the latter implies the former, and that a definition must exist to fulfill that implication. In many cases, defining the latter implicitly defines the former anyway, but the compiler cannot deduce that in general, and thus requires both instances to be written out explicitly. The same thing can be observed with Eq and Ord--the latter obviously implies the former, but you still need to define an Eq instance in order to define one for Ord.

妄断弥空 2024-11-25 01:28:32

IO 类型在 Haskell 中是抽象的,因此如果您想为 IO 实现通用的 Applicative,您必须使用以下操作来实现:由IO支持。由于您可以根据 Monad 操作来实现 Applicative,这似乎是一个不错的选择。你能想出另一种方法来实现它吗?

是的,Applicative 在某种意义上不如 Monad 强大。

The IO type is abstract in Haskell, so if you want to implement a general Applicative for IO you have to do it with the operations that are supported by IO. Since you can implement Applicative in terms of the Monad operations that seems like a good choice. Can you think of another way to implement it?

And yes, Applicative is in some sense less powerful than Monad.

农村范ル 2024-11-25 01:28:32

Applicative 是不是一个不如 Monad 强大的概念?

是的,因此每当您有一个 Monad 时,您始终可以将其设为 Applicative。您可以将 IO 替换为示例中的任何其他 monad,它将是一个有效的 Applicative 实例。

打个比方,虽然彩色打印机可能被认为比灰度打印机更强大,但您仍然可以使用彩色打印机来打印灰度图像。

当然,也可以将 Monad 实例基于 Applicative 并设置 return = pure,但您将无法定义 <一般情况下,代码>>>=。这就是 Monad 变得更强大的意义。

Isn't Applicative a less powerful concept than Monad?

Yes, and therefore whenever you have a Monad you can always make it an Applicative. You could replace IO with any other monad in your example and it would be a valid Applicative instance.

As an analogy, while a color printer may be considered more powerful than a grayscale printer, you can still use one to print a grayscale image.

Of course, one could also base a Monad instance on an Applicative and set return = pure, but you won't be able to define >>= generally. This is what Monad being more powerful means.

谜兔 2024-11-25 01:28:32

完美世界中,每个Monad都是一个Applicative< /code> (所以我们有 class Applicative a => Monad a where ...),但由于历史原因,两个类型类都是独立的。因此,您认为这个定义有点“倒退”(使用更强大的抽象来实现不太强大的抽象)的观察是正确的。

In a perfect world every Monad would be an Applicative (so we had class Applicative a => Monad a where ...), but for historical reasons both type classes are independend. So your observation that this definition is kind of "backwards" (using the more powerful abstaction to implement the less powerful one) is correct.

红尘作伴 2024-11-25 01:28:32

对于旧版本的 GHC,您已经有了完美的答案,但在最新版本中,您实际上确实有 class Applicative m =>; Monad m 所以你的问题需要另一个答案。

就 GHC 实现而言:GHC 只是在尝试编译任何实例之前检查为给定类型定义了哪些实例。

从代码语义上来说: class Applicative m => Monad m 并不意味着必须“首先”定义 Applicative 实例,只是如果在程序结束时尚未定义它,那么编译器将中止。

You already have perfectly good answers for older versions of GHC, but in the latest version you actually do have class Applicative m => Monad m so your question needs another answer.

In terms of GHC implementation: GHC just checks what instances are defined for a given type before it tries to compile any of them.

In terms of code semantics: class Applicative m => Monad m doesn't mean the Applicative instance has to be defined "first", just that if it hasn't been defined by the end of your program then the compiler will abort.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文