为什么Foldable和Functor分开了?

时间:2015-10-14 23:47:22

标签: haskell

wiki page表示这两个类都处理容器操作,Foldable是一类容器,它们定义了foldr,而Functor则是fmap

但是,可折叠类型与Functors类型之间的根本区别是什么?

维基暗示了这种区别:

  

类[Foldable]不需要Functor超类来允许像Set或StorableVector这样的容器

但是我仍然不确定如果我正确地解释了为什么Set无法被映射以获得另一个集合。

5 个答案:

答案 0 :(得分:12)

FoldableFunctor为结构可以折叠(或简化)和分别映射到。

A Foldable包含可以枚举并组合在一起的值 1 。人们可以将可折叠视为可以转化为列表的内容(toList :: Foldable f => f a -> [a])。或者,可以将Foldables视为其值可以单独组合的结构:(Foldable t, Monoid m) => (a -> m) -> t a -> m(当然,这需要能够枚举它们)。

另一方面,Functors是允许一个人提升"函数(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

  1. "保持"有点松散,意图在"Functors are containers"意义上解释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没有任何有意义的方式。

值得一提的是,FoldableFunctorTraversable类中聚集在一起,这比任何一个都要强大得多。

答案 2 :(得分:7)

SetStorableVector 仿函数,您确实可以在其上映射函数。

但不是任何类型的功能。例如,您无法在(+)个数字上映射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需要OrdStorableVector需要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