GHC具有几个有用的语言的扩展,用于机械地导出各种常见的Haskell类型类(-XDeriveFunctor
,-XDeriveFoldable
,-XDeriveTraversable
).似乎这Applicative
是另一个经常需要并经常容易衍生出来的课程.对于包含类型插槽的简单记录a
,例如,
data SimpleRecord a = Simple a a a
Run Code Online (Sandbox Code Playgroud)
该Applicative
实例是非常简单的,
instance Applicative SimpleRecord where
pure x = Simple x x x
Simple a1 b1 c1 <*> Simple a2 b2 c2 = Simple (a1 a2) (b1 b2) (c1 c2)
Run Code Online (Sandbox Code Playgroud)
即使在稍微更难的情况下,某些a
值被埋在其他应用程序中,例如,
data MyRecord f a = MyRecord (f a) a
Run Code Online (Sandbox Code Playgroud)
一个合理的实例很容易写,
instance (Applicative f) => Applicative (MyRecord f) where
pure x = MyRecord (pure x) x
MyRecord a1 b1 <*> MyRecord a2 b2 = MyRecord (a1 <*> a2) (b1 b1)
Run Code Online (Sandbox Code Playgroud)
为什么-XDeriveApplicative
不存在实现这些类型的机械实例的扩展?即使是derive
和generic-derive
包装显然缺乏Applicative
支持.有没有被有效期一般是一个理论问题排除这些情况下(超出也可能威胁到这些原因Functor
,Foldable
或Traversable
扩展)?
Jon*_*rdy 14
Functor
对于给定数据类型,最多只有一个实例遵循仿函数定律.例如,map
是fmap
列表的唯一合法实现:
fmap id == id
fmap (f . g) == fmap f . fmap g
Run Code Online (Sandbox Code Playgroud)
但是可以有不止一个守法的例子Applicative
,这不一定是显而易见的.
pure id <*> v == v
pure (.) <*> u <*> v <*> w == u <*> (v <*> w)
pure f <*> pure x == pure (f x)
u <*> pure y == pure ($ y) <*> u
Run Code Online (Sandbox Code Playgroud)
对于列表,<*>
可以表现得像\fs xs -> concatMap (\f -> map f xs) fs
或类似zipWith ($)
,并且不清楚编译器应该选择哪一个.
为了回应别人,没有充分的理由我知道为什么我们不能拥有-XDeriveApplicative
,我们根本就没有.通常有不止一个合法的实例Foldable
和Traversable
,我们有一个标志来获得这些.有一段时间我们没有关于可穿越法律的真实好故事,但现在我们有了一些.同样,我们仍然没有Foldable
法律(但我认为我们可以,见这里).
在不同的"明显"应用中,例如向前和向后的应用程序(对于<*>
自身,甚至a
是f a
在有这样的情况下对多个进行置换),然后按照句法顺序遍历构建这里的应用程序,似乎合法.但是,对于诸如列表之类的递归类型,甚至是具有多个构造函数的类型,我们选择有效的应用程序都会以有趣的方式开花.对于类似列表的常规递归类型,明显的应用选择自然是"zipLike"一次,因为它以自然的方式推广非递归情况,将结构与结构匹配.对于具有多个构造函数的sum类型,"明显"选择更难定义.
在我看来,一个完全合理的applicative派生将按照这里的建议工作,在语法顺序上只有一个构造函数的类型(包括递归的).在多个构造函数的情况下,失败似乎是合法的.
另一方面,尽管Foldable和Traversable似乎经常出现在他们的"明显"形式中,但对于我来说,与有趣的相比,我们希望定义多少次"明显"的应用程序并不是很清楚.我的直觉告诉我,这个功能很少被运用,也许根本不常用.
归档时间: |
|
查看次数: |
556 次 |
最近记录: |