haskell - 为什么 ap 在 Applicative 中可用?
问题描述
我正在尝试为 Snap 实现 MonadUnliftIO 并分析 Snap 类。我发现 ap 用于实现 Applicative 而 ap 需要 Monad 而 Monad 需要 Applicative。它看起来像一个循环。
直到现在我都认为不可能写出这样的东西。这种把戏的极限是什么?
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
instance Applicative Snap where
pure x = ...
(<*>) = ap
ap :: Monad m => m (a -> b) -> m a -> m b
解决方案
这只有效,因为Snap
有一个Monad
实例(并且它实际上在那时的范围内)。
实际上,编译器在两个单独的过程中处理声明:首先它解析所有实例头
instance Applicative Snap
instance Monad Snap
...甚至没有查看实际的方法实现。这很好:Monad
只要看到Applicative
实例就很高兴。
所以它已经知道这Snap
是一个单子。然后它继续对(<*>)
实现进行类型检查,注意到它需要Monad
实例,并且......是的,它就在那里,所以这也很好。
我们拥有的实际原因ap :: Monad m => ...
主要是历史原因:Haskell98Monad
类没有Applicative
,甚至没有Functor
超类,因此可以编写Monad m => ...
不能使用fmap
or的代码<*>
。因此引入了liftM
和ap
函数作为替代。
然后,当建立更好的当前类层次结构时,通过引用已经存在的实例来简单地定义许多实例Monad
,这对于所有事情来说已经足够了。
IMO 通常在编写实例之前<*>
直接实现并且绝对是一个好主意,而不是相反。fmap
Monad
推荐阅读
- regex - 使用正则表达式将字符串转换为 JSON
- c# - 从 C# 重命名网页
- javascript - 具有 useContext() 的子项未使用当前值重新呈现
- python - 匹配所有内容,直到括号内有数字
- delphi - 如何检测窃取特定快捷方式的未知对象?
- java - 当 EditText 为空白并按下按钮时,Android 应用程序崩溃
- javascript - 在 React TypeScript 中键入输入的 keyup
- wcf - 无法在 ASP.NET Core 5.0 下的 IIS 中托管 CoreWcf
- angular - 我想将日期转换为 dd-mm-yyyy
- python - 尝试从 gstreamer appsink 获取 numpy 图像导致缓冲区太小错误