首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Traversable1中的上下文界限

Traversable1中的上下文界限
EN

Stack Overflow用户
提问于 2013-05-12 01:37:52
回答 1查看 114关注 0票数 7

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

代码语言:javascript
复制
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

为什么上下文界限设置为Apply (不带pureApplicative )而不是Functor?显然,您需要覆盖其中的一个定义,所以这对于“仅仅”一个Functor是不可能的吗

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-05-12 02:16:33

这只是对签证的稍微严格的定义-所有的Traversable1都是Traversable,但不是相反。有关Traversable为什么需要Applicatives的更多详细信息,也许值得一看Applicative Programming with Effects。从手势上讲,如果你只有一个Functor,那么如果它包含很多值,就不可能对该函数的效果进行“排序”,因为“注入”函数(a -> f b)是获得bs的唯一方法,并且你不能join你的f的层。

但是,从广义上讲,当您定义Traversables时,您只需要对“默认”值使用无影响的注入函数pure,这正是Traversable1所消除的。这就是为什么NonEmpty是一个实例,而[]不是。

具体来说,考虑这些标识函数器、MaybeNonEmpty列表和常规[]的示例实例。

代码语言:javascript
复制
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只有一个元素,没有“默认”分支--这很简单。

代码语言:javascript
复制
instance Traversable Id where traverse inj (Id a) = Id <$> inj a
instance Traversable1 Id where traverse1 inj (Id a) = Id <$> inj a

对于Maybe的“默认”Nothing情况,我们需要pure (它只比Id稍微复杂一点)。

代码语言:javascript
复制
instance Traversable Maybe where
  traverse _ Nothing = pure Nothing
  traverse inj (Just a) = Just <$> inj a

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

代码语言:javascript
复制
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实例

代码语言:javascript
复制
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作为“默认”分支

代码语言:javascript
复制
instance Traversable [] where
  traverse _   []     = pure []
  traverse inj (x:xs) = (:) <$> inj x <*> traverse inj xs

编辑:最初,我对Traversable1 NonEmpty的定义是游刃有余的。目前的版本实际上是有效的,但对眼睛来说要难得多。之前我尝试过traversing内部列表,它在精神上工作,因为NonEmpty的第二个插槽中的[]有第一个插槽来帮助它,但这不能直接工作,因为内部列表有一个空的[],它需要pure。相反,我们必须通过“窃取”第一个位置中始终存在的a,然后在遍历之后替换它来避免那个空案例。

这种方法(和数据类型定义)与半组和半群集库本身使用的版本非常相似,并且非常有用,因为它们可以利用常规[]背后的库动力,但如果我们对NonEmpty的定义稍有不同,我们可以看到TraversableTraversable1之间有很大的并行性。Traversable1实例可以存在的事实确实是数据类型本身的一个特性-定义基本上是相同的。

代码语言:javascript
复制
import Data.Monoid
import qualified Data.Semigroup as Se
import Data.Traversable
import Data.Foldable
import Data.Semigroup.Foldable
import Data.Semigroup.Traversable
import Data.Functor.Apply
import Control.Applicative

-- For comparison
data List     a = Empty | List a (List     a)
data NonEmpty a = One a | Many a (NonEmpty a)

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

instance Apply NonEmpty where
  (One f) <.> (One a)         = One (f a)
  (One f) <.> (Many a _)      = One (f a)
  (Many f _) <.> (One a)      = One (f a)
  (Many f fs) <.> (Many a as) = Many (f a) (fs <.> as)

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

instance Foldable NonEmpty where
  foldMap f (One a) = f a
  foldMap f (Many a as) = f a <> foldMap f as

instance Foldable1 NonEmpty where
  foldMap1 f (One a) = f a
  -- Core distinction: we use the Semigroup.<> instead of the Monoid.<>
  foldMap1 f (Many a as) = f a Se.<> foldMap1 f as

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

instance Traversable1 NonEmpty where
  traverse1 inj (One a) = One <$> inj a
  traverse1 inj (Many a as) = Many <$> inj a <.> traverse1 inj as
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16499962

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档