wiki page表示这两个类都处理容器操作,Foldable是一类容器,它们定义了foldr
,而Functor则是fmap
。
但是,可折叠类型与Functors类型之间的根本区别是什么?
维基暗示了这种区别:
类[Foldable]不需要Functor超类来允许像Set或StorableVector这样的容器
但是我仍然不确定如果我正确地解释了为什么Set无法被映射以获得另一个集合。
答案 0 :(得分:12)
Foldable
和Functor
为结构可以折叠(或简化)和分别映射到。
A Foldable包含可以枚举并组合在一起的值 1 。人们可以将可折叠视为可以转化为列表的内容(toList :: Foldable f => f a -> [a]
)。或者,可以将Foldables视为其值可以单独组合的结构:(Foldable t, Monoid m) => (a -> m) -> t a -> m
(当然,这需要能够枚举它们)。
(a -> b)
应用于结构(a
)持有的fmap :: (a -> b) -> (f a -> f b)
。 fmap
必须保留映射的结构:树必须在前后具有相同的形状,列表必须具有相同数量的元素,Nothing
才能被转换变成某种东西,等等。另一方面,Foldables不需要保留这种结构;重点是丢弃结构并生成新结构。
Wiki指的是没有办法向fmap
提供类型类约束。 fmap :: (Ord a, Ord b) => (a -> b) -> Set a -> Set b
未与类fmap :: (a -> b) -> f a -> f b
定义的类型统一,该类没有约束。这使Set
的实例无法编写。
然而,这只是一个语言实现问题,而不是关于集合的更深入的数学陈述。 Foldable没有Functor超类的真正原因只是there are Foldable instances which are not Functor instances。
Proxy s a
保持零,Identity a
保持一个a,Maybe a
保持零或一个a,b -> a
拥有|b|
个,等等。答案 1 :(得分:8)
假设我有xs :: Set Int
,我想将函数putStrLn
映射到它上面。这将是一个问题,因为IO ()
没有Ord
实例,因此无法弄清楚如何将这些操作插入到结果集中。 fmap
的类型不允许对Functor
的类型参数进行约束。
IO
还提供了一个Functor
的示例,但foldMap
没有任何有意义的方式。
值得一提的是,Foldable
和Functor
在Traversable
类中聚集在一起,这比任何一个都要强大得多。
答案 2 :(得分:7)
Set
和StorableVector
是仿函数,您确实可以在其上映射函数。
但不是任何类型的功能。例如,您无法在(+)
个数字上映射StorableArray
:这会产生一系列函数,而这些函数无法存储。
因此,这些仿函数不是所有 Hask 上的(endo-)仿函数,而只是在包含特定类类型的子类别上。这不能在Haskell98中表达,事实上,随着约束种类的出现,它最近才成为可能。 See this example:
instance Functor Set Ranking Ranking where
fmap = constrainedFmap Set.map
实际上,如果使用一些聪明的GADT tricks,集合也会在 Hask 中形成一个monad。但是对于所有Foldable
容器来说,这是不可能的,因此标准库不需要Functor f => Foldable f
。
答案 3 :(得分:5)
Functor
类型类不允许fmap
超过对其元素类型有约束的事物。 Set
需要Ord
,StorableVector
需要Storable
。
可以使用GHC的ConstraintKinds
扩展来表达“受限仿函数”,例如:
{-# LANGUAGE ConstraintKinds,
FunctionalDependencies,
MultiParamTypeClasses #-}
import GHC.Exts (Constraint)
import Data.Set (Set)
import qualified Data.Set as Set
class ConstrainedFunctor c f | f -> c where
cfmap :: (c a, c b) => (a -> b) -> f a -> f b
instance ConstrainedFunctor Ord Set where
cfmap = Set.map
但Functor
首次出现时,这种机制并不存在。
此外,Functor
应该是“保持形状”,但例如Set
上的映射可能会改变其大小。
答案 4 :(得分:0)
遍历是map的概括。遍历还可以表示foldMap(因此foldLeft / foldRight)。因此Traverse既是函子又是可折叠的:
https://www.slideshare.net/pjschwarz/sequence-and-traverse-part-3