【问题标题】:Is it possible to create a Functor instance for sorted binary trees in Haskell?是否可以在 Haskell 中为排序的二叉树创建 Functor 实例?
【发布时间】:2013-11-30 05:31:37
【问题描述】:

假设我们有一个SortBinTree 类型的构造函数,例如,

data SortBinTree a = EmptyNode | Node a (SortBinTree a) (SortBinTree a);

仅当aOrd 类型类的实例时才有意义,因此大多数函数在其声明的开头都有:: (Ord a) =>,尤其是用于从列表创建此类树的函数。但是要教 Haskell,SortBinTreeFunctor 类型类的一个实例,我们必须编写类似

instance Functor SortBinTree where
  fmap g tree = ...

这里的问题是我们要处理g :: a->b,其中b不一定是Ord类型类的实例。这使得编写这样的函数有问题,因为我们不能在创建 SortBinTree b 类型的元素时使用不等式。

这里有标准的解决方法吗?有什么方法可以定义fmap 仅针对b 的情况在Ord 类型类中?

【问题讨论】:

    标签: design-patterns haskell functional-programming functor


    【解决方案1】:

    不,Functor 类型类无法做到这一点。如您所述,the Prelude gives us¹

    class Functor f where
      fmap :: (a -> b) -> f a -> f b
    

    它无法对b 施加限制。我们可以定义一个OrdFunctor 类:

    class OrdFunctor f where
      fmapOrd :: (Ord a, Ord b) => (a -> b) -> f a -> f b
    

    如果我们有很多不同类型的Functors(EqFunctorMonoidFunctor 等),这可能会很烦人,但是如果我们打开 ConstraintKindsTypeFamilies,我们可以将其概括为受限仿函数类:

    {-# LANGUAGE ConstraintKinds, TypeFamilies #-}
    import GHC.Exts (Constraint)
    import Data.Set (Set)
    import qualified Data.Set as S
    
    class RFunctor f where
      type RFunctorConstraint f :: * -> Constraint
      fmapR :: (RFunctorConstraint f a, RFunctorConstraint f b) => (a -> b) -> f a -> f b
    
    -- Modulo the issues with unusual `Eq` and `Ord` instances, we might have
    instance RFunctor Set where
      type RFunctorConstraint f = Ord
      fmapR = S.map
    

    (通常,您会看到有关受限单子的内容;这是相同的想法。)

    或者,as jozefg suggested,您可以编写自己的 treeMap 函数而不将其放入类型类中。没有错。

    但是请注意,在将 SortBinTree 设为仿函数时应该小心;以下是不是fmap。 (不过,deriving (..., Functor) 会产生它,所以不要使用它。)

    notFmap :: (Ord a, Ord b) => (a -> b) -> SortBinTree a -> SortBinTree b
    notFmap f EmptyNode    = EmptyNode
    notFmap f (Node x l r) = Node (f x) (notFmap l) (notFmap r)
    

    为什么不呢?考虑notFmap negate (Node 2 (Node 1 EmptyNode EmptyNode) EmptyNode)。这将产生树Node (-2) (Node (-1) EmptyNode EmptyNode) EmptyNode),这可能违反了你的不变量——它是向后排序的。²所以确保你的fmap是保持不变的。 Data.Set 将这些拆分为 map, which makes sure the invariants are preserved,mapMonotonic, which requires you to pass in an order-preserving function。后一个函数的实现很简单,就像notFmap,但如果给定不合作的函数,可能会产生无效的Sets。


    ¹The Data.Functor module also exposes the (<$) :: Functor f => a -> f b -> a type class method,但这只是为了防止fmap . const 有更快的实现。

    ² 但是,notFmap fmap 来自 Hask 的子类别,其对象是具有 Ord 实例的类型,并且其态射是 order -preserving 映射Hask 的子类别,其对象是 SortBinTrees 超过具有 Ord 实例的类型。 (模数关于“不合作”Eq/Ord 实例的一些潜在担忧,例如那些将Set 混淆为Functor 的实例。)

    【讨论】:

    • 关于脚注 2。是的,它确实形成了一个函子,从 Hask 的有序子类别到 SortBinTree 的子类别和有序 as。本质上,Functor 是一个适当的内函子 F : Hask -> Hask,但我们想要一个更精细的 RestrictedF : exists C <= Hask, D <= Hask, C -> D 概念,它是 Hask 的子类别之间的函子。我们没有这个的原因纯粹是一个历史事故,因为它提供了我们当前 Functor 的严格超集。
    • @jozefg 啊,谢谢。我从您的评论中修复了易于修复的部分。
    【解决方案2】:

    有两种选择,如果你的类型满足函子定律,那么正确的技巧是

    {-# LANGUAGE DeriveFunctor #-}
    data SortBinTree a = EmptyNode
                       | Node a (SortBinTree a) (SortBinTree a)
                       deriving Functor
    -- Or a manual instance if you have some invariants that
    -- need additional jiggering.
    

    并确保它的所有操作都需要Ord 实例。如果有人决定让这棵树处于无用状态,那么修复它就是他们自己的工作。

    但是,要使其正常工作,您必须满足函子定律

     fmap id         === id
     fmap f . fmap g === fmap (f . g)
    

    因此,如果您从树中完全删除重复项,您将位于trouble。这就是为什么Data.SetFunctor 的一个实例是可疑的,它违反了这条法律。

    如果你违反了法律,那么你根本就不是函子。您不能向 Haskell 指定您只想处理 Hask 的子类别。在这种情况下,您应该只定义一个不同的函数

    treeMap :: (Ord a, Ord b) => (a -> b) -> SortBinTree a -> SortBinTree b
    

    在范畴论意义上,这仍然是一个函子,只是不是Functor 所说的那个。

    【讨论】:

    • Set的标准是什么?你的意思是Data.Set?然后看起来,它不是 Haskell 的 Functor 类型类的成员。如果是的话,请你给我一些参考吗?
    • @jozefg 但正如 fiktor 所说,Set 不是 Functor,而无法无天正是原因所在。
    • @AntalS-Z 对不起,我误读了文档,我已经更新了我的答案,使其更加清晰。
    • @fiktor 抱歉,我错过了您的评论编辑,我已经更新以明确说明。
    • 在你引用的minimal reproducible example[1] 中,fmap 打破了函子的组合定律,只是因为unWrap 打破了数学定律(如果 a=b 则 f(a) = f(b) )。所以我认为Data.Set 应该是Functor 的一个实例,因为它满足所有“正常”功能的Functor 法则。 [1]:stackoverflow.com/questions/19177125/…
    猜你喜欢
    • 2018-06-30
    • 2021-05-21
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 2019-04-09
    • 2018-05-02
    • 2017-09-28
    相关资源
    最近更新 更多