Traversable1中的上下文边界

时间:2013-05-11 17:37:52

标签: haskell

semigroupoids包中,我找到了以下定义:

class (Foldable1 t, Traversable t) => Traversable1 t where
  traverse1 :: Apply f => (a -> f b) -> t a -> f (t b)
  sequence1 :: Apply f => t (f b) -> f (t b)

  sequence1 = traverse1 id
  traverse1 f = sequence1 . fmap f

为什么上下文边界设置为ApplyApplicative没有pure)而不是Functor?显然你需要覆盖其中一个定义,所以“仅仅”Functor这是不可能的吗?

1 个答案:

答案 0 :(得分:6)

这只是Traversable的一个略微收紧的定义---所有Traversable1都是Traversable,但反之亦然。关于Traversable需要Applicative的原因的许多(很多)详细信息,或许值得一看Applicative Programming with Effects。从根本上说,如果你只有一个Functor,那么如果它包含很多值就不可能“排序”该仿函数的效果,因为你的“注入”函数(a -> f b)是获得{的唯一方法{1}}并且您不能b join的图层。

但是,广泛地说,当您定义f时,您只需要使用无效注入函数Traversable来表示“默认”值,这正是pure消除的。这就是为什么Traversable1是一个实例但NonEmpty不是。

的原因

为了使事情具体化,请考虑身份仿函数的这些示例实例,[]Maybe列表和常规NonEmpty

[]

我们这里只需要一个newtype Id a = Id a instance Functor Id where fmap f (Id a) = Id (f a) instance Applicative Id where pure = Id (Id f) <*> (Id x) = Id (f x) 个实例,因为Functor只有一个元素而没有“默认”分支 - 它非常简单。

Id

我们需要instance Traversable Id where traverse inj (Id a) = Id <$> inj a instance Traversable1 Id where traverse1 inj (Id a) = Id <$> inj a pure Nothing Maybe的情况(仅比Id稍微复杂一点)。

instance Traversable Maybe where
  traverse _ Nothing = pure Nothing
  traverse inj (Just a) = Just <$> inj a

instance Traversable1 Maybe不能存在,因为Maybe有默认分支;我们看到了这一点,因为如果我们只有pure约束,我们就无法使用Apply

data NonEmpty a = NonEmpty a [a]

instance Functor NonEmpty where fmap f (NonEmpty a as) = NonEmpty (f a) (fmap f as)

instance Apply NonEmpty where
  (NonEmpty f fs) <.> (NonEmpty x xs) = NonEmpty (f x) (fs <*> xs)

instance Pointed NonEmpty where
  point a = NonEmpty a []

instance Applicative NonEmpty where
  (<*>) = (<.>)
  pure = point

instance Traversable NonEmpty where
  traverse inj (NonEmpty a as) = NonEmpty <$> inj a <*> (traverse inj a as)

由于我们只使用了(<*>)而非pure,因此我们可以将其设为Traversable1个实例

instance Traversable1 NonEmpty where
  traverse1 inj (NonEmpty a []) = (`NonEmpty` []) <$> inj a
  traverse1 inj (NonEmpty a (b: bs)) = 
    (\a' (NonEmpty b' bs') -> NonEmpty a' (b': bs')) 
    <$> inj a 
    <.> traverse1 inj (NonEmpty b bs)

但这对[]不起作用,因为我们最终使用pure作为“默认”分支

instance Traversable [] where
  traverse _   []     = pure []
  traverse inj (x:xs) = (:) <$> inj x <*> traverse inj xs

编辑:最初我根据我对Traversable1 NonEmpty的定义玩得很快。目前的版本确实有效,但眼睛更难。之前我尝试了traversing内部列表,它在精神上有效,因为[]的第二个广告位中的NonEmpty有第一个帮助它的插槽,但这不能直接使用,因为内部列表有一个空案例[],需要pure。相反,我们必须通过在第一个位置“窃取”始终存在的a然后在遍历之后替换它来避免该空案例。

该方法(和数据类型定义)与Semigroups和Semigroupoids库本身使用的版本非常相似,并且非常有用,因为它们可以利用常规[]后面的库动量,但是如果我们定义{{1有点不同,我们可以看到NonEmptyTraversable之间存在大量的并行性。 Traversable1实例可以存在的事实确实只是数据类型的一个特征 - 定义基本相同。

Traversable1