【问题标题】:Why are Foldable and Functor separate classes?为什么 Foldable 和 Functor 是分开的类?
【发布时间】:2015-10-14 23:47:22
【问题描述】:

wiki page 表示这两个类都处理容器操作,Foldable 是一类在其上定义了 foldr 的容器,而对于 Functor,它是 fmap

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

wiki 暗示了这种区别:

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

但我仍然不确定为什么不能将一个 Set 映射到另一个集合上,如果我解释正确的话。

【问题讨论】:

  • @Bergi 哎呀,写的很着急,加了链接

标签: haskell


【解决方案1】:

FoldableFunctor 为具有可折叠(或缩减)和映射结构的类型提供两种独立的抽象,分别。

Foldable 包含可以枚举和组合在一起的值1。可以将 Foldable 视为可以变成列表的东西 (toList :: Foldable f => f a -> [a])。或者,可以将 Foldables 视为其值可以单向组合的结构:(Foldable t, Monoid m) => (a -> m) -> t a -> m(当然,这需要能够枚举它们)。

另一方面,函子是允许“提升”函数(a -> b) 以应用于结构所持有的as (fmap :: (a -> b) -> (f a -> f b)) 的结构。 fmap 必须保留被映射的结构:树必须具有前后相同的形状,列表必须具有相同数量的相同顺序的元素,Nothing 不能变成某种东西,等等.另一方面,可折叠设备不需要保留这种结构;关键是要丢弃结构并产生一个新结构。

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 保持零 a,Identity a 保持一个 a,Maybe a 保持零或一个 a,b -> a 保持|b|a's,等等。

【讨论】:

    【解决方案2】:

    假设我有xs :: Set Int,我想在它上面映射函数putStrLn。这将是一个问题,因为IO () 没有Ord 实例,因此无法弄清楚如何将这些操作插入结果集中。 fmap 的类型对 Functor 的类型参数没有限制。

    IO 还提供了一个 Functor 的示例,但 foldMap 没有任何有意义的方式。

    值得一提的是,FoldableFunctorTraversable 类中组合在一起,这比两者都提供了更多的功能。

    【讨论】:

    • 为什么需要Ord 来弄清楚如何将这些操作插入到结果集中?您是否只需要Eq,例如== 对于任何两个IO () 总是可以是False
    • @jcora,仅使用Eq 来实现高效 集是不可能的。为IO 制作一个Eq 的虚拟实例将打破程序员对== 含义的所有期望。
    • 当然,无论您需要Eq 还是Ord,都没有抓住重点。关键是你不能有 any 类型类约束。
    • @ReinHenrichs,我认为指出为什么对fmap 的任何特定限制都可能有害是有用的。
    • @jcora 始终返回 false 的 == 不是有效的相等关系(它要求事物与自身相等)。如果你还是这样做了,那么将其用作 Set 的基础是没有用的,因为这意味着在测试集合中某个项目的成员资格时,该项目永远不会等于集合中的任何内容,因此成员资格测试将总是返回假。这意味着要保持一致,任何此类集合都必须为空;您甚至无法从中检索项目,因为任何生成的项目都将无法通过成员资格测试,因此实际上根本不在集合中。
    【解决方案3】:

    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

    【讨论】:

      【解决方案4】:

      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 可能会改变其大小。

      【讨论】:

        【解决方案5】:

        Traverse 是地图的概括。 Traverse 也可以表示 foldMap(& 所以 foldLeft/foldRight)。所以 Traverse 既是 Functor 又是 Foldable:

        https://www.slideshare.net/pjschwarz/sequence-and-traverse-part-3

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-30
          • 1970-01-01
          • 1970-01-01
          • 2014-03-06
          相关资源
          最近更新 更多