【发布时间】:2011-01-08 09:30:04
【问题描述】:
应用程序的两个著名示例是 monad 和 ziplist。还有其他例子吗?
【问题讨论】:
标签: haskell functional-programming
应用程序的两个著名示例是 monad 和 ziplist。还有其他例子吗?
【问题讨论】:
标签: haskell functional-programming
来自Time flies like an applicative functor,Conor McBride:
结构警察会注意到
De是应用函子的另一个例子,它不是一个 monad——join 会将事情从遥远的未来带到不远的未来,而这最好是不可能的。然而,应用函子通常只通过可遍历函子(具有有限多个元素的容器),De会通过所有容器。所以有点特别。我想知道它是什么。
和
De函子表示固定延迟,而不是任意延迟。我把时间分成离散的片断。De x是在下一个切片到期的x的类型。因此,De (De x)是x的类型,在两片时间内到期,你不能让它早点出现!
阅读整篇文章。为了回答直接的问题,作者的结论是
别看!
好的,这是实现。这是一个骗局。
newtype De x = De x deriving Show -- ssh, don't tell! instance Functor De where fmap f (De x) = De (f x) instance Applicative De where pure = De De f <*> De s = De (f s) fix :: (De x -> x) -> x fix f = f (De (fix f))
【讨论】:
我最近在(,,,) 之上为一个新类型定义了一个Applicative 实例,一个“quad”。 (标准库为(,) 定义了一个实例,但不是(,,,)。没关系,因为标准实现的语义与我所追求的不同。)
背景是;我正在解析一些旧数据,数据中的日期格式不明确。数据中的每个日期都可以解析为四种可能性,存储在四边形中。然后我想验证四边形中的每个日期以消除语义上无效的日期。 (没有 32 天的月份,没有 34 个月,没有第 5 季度等)最后,我想取数据集中的每个日期,并将整个集合缩减为一个四边形,表示哪些日期格式是有效的对于整个集合。然后,我从这些选项中选择最佳格式,并假设这就是数据集的日期格式。
整个操作很容易表达为四边形结构上的应用操作。
这是代码的基本形状:
我的新类型:
newtype DQ a = DQ (a, a, a, a) -- date quad
deriving ...
instance Functor DQ where
g `fmap` f = pure g <*> f
instance Applicative DQ where
pure x = DQ (x, x, x, x)
DQ (g, h, i, j) <*> DQ (a, b, c, d) = DQ (g a, h b, i c, j d)
一些先决条件“纯”功能:
parseDateInt :: Int -> DQ Date
validateDate :: Date -> Bool
extractBestDate :: DQ Date -> DQ Bool -> Date
因此,一旦我们获得了已解析日期的四边形(来自 parseDateInt),我们需要验证它们:
validateDates :: DQ Date -> DQ Bool
validateDates = (validateDate <$>)
(到目前为止,这只是一个 Functor,但你也可以写
(pure validateDate <*>).
还值得注意的是单个验证之间的对称性
元素,并验证集合中的每个元素——为了验证一个元素,你可以写
validateDate $ date;为了验证一个集合,你写
validateDate <$> dates。这就是为什么fmap 写成<$>,它是函子的函数应用。)
这之后的步骤是获取一组有效的解析并将其折叠 最终结果:
intuitDateType :: [DQ Bool] -> DQ Bool
intuitDateType dates = foldl1 (liftA2 (&&)) dates
所以现在您可以从数据文件中的[Int] 转到DQ Bool
表示数据集的可能有效的日期表示。
(然后从那里,将每个数据点与一个真实的日期对象相关联,
而不是提供的片状 Int。)
所以无论如何,这篇文章有点长,但我的想法是
应用实例允许我用大约 3 行来解决我的问题
的代码。我的问题域是反复将函数应用于数据
容器,这是 Applicative 函子所做的。该数据没有join 操作,因此Monad 实例没有多大意义。
【讨论】:
join ((a, _, _, _), (_, b, _, _), (_, _, c, _), (_, _, _, d)) = (a, b, c, d)(直到newtypes)。它与Reader T monad 同构(可能只忽略_|_s),其中T 是具有四个值的类型。
Conal Elliott writes about signal processors and how they're applicatives。它们在本质上类似于ZipList,其中两个“容器”中的每一对项目都被组合在一起。
我在我制作的一个未完成但很可爱的游戏中经常使用这个概念(cabal install DefendTheKing 来看看)。
代码 sn-p/Applicative 样式用法示例:
draw font
<$> lstP gABoard
<*> lstP gASelection
<*> mouseMotion
<*> lstP gASide
<*> lstP gAGameIteration
【讨论】:
Formlets 是对 HTML 表单的抽象,以组合应用程序的形式描述。 formlet 应用程序是由名称生成应用程序(生成表单元素名称)、XML 编写器应用程序(生成 HTML)和环境应用程序(提供提交的表单值)组成的结果。
Formlets 可以通过组合额外的应用程序来扩展,例如实现验证。
Cooper、Wadler 等人在论文中表明,formlets 不能表示为 monad。
Formlets 已在 Haskell 中实现,here is the package。
【讨论】:
Swierstra 和 Duponcheel 定义了一种高效的解析器风格,这种解析器几乎是 Arrows 的早期典型代表,但它不需要 Arrows 提供的任何 Applicative 无法提供的东西。但是,当时还没有创造出 Applicatives。
它有效地为 LL(1) 解析器计算“FIRST”集,并使用它来进行更智能的分支选择。但是,当您以单子方式工作时,您无法计算这些集合。
这可能不是一个非常公平的例子,因为 Swierstra/Duponcheel 解析器承认静态和动态解析器的混合,并且只有静态解析器被限制为适用性。
通过可观察共享,您可以将他们的解析器设计推得更远,还可以计算“FOLLOW”集(只要您注意不要构建无限上下文无关语法。)。这为解析上下文无关语法提供了很好的渐近保证,而在使用单子(上下文敏感)解析器设计进行解析时,您将无法使用这些语法。
也许同样有趣的是,考虑应用的 可用的结构,但不是纯的。许多comonads承认类似()的定义,它尊重comonads的结构,但对“纯”没有合理的定义。我的 semigroupoids 包和依赖它的无数包进一步探索了这个想法。
【讨论】:
我相信箭头是应用函子。 Control.Applicative 中肯定有 WrapArrow 类型。
【讨论】:
McBride 和 Paterson http://www.soi.city.ac.uk/~ross/papers/Applicative.pdf 表明可以将幺半群视为应用函子,但一般情况下它不是单子。
【讨论】: