一般来说,可折叠仿函数的头/尾是否等同?

时间:2018-06-10 15:27:19

标签: haskell functor shift traversable foldable

我想表达以下Haskell代码,只使用仿函数代数(即 - 依赖于任何特定的容器类型,例如List) :

ys = zipWith (+) (head xs : repeat 0)
                 (tail xs ++ [y])

在我看来,应该有办法做到这一点,仅依靠Foldable(或许,Traversable),但我无法看到它。

我想知道:

  1. 对于可折叠/可遍历的仿函数,是否存在 first rest 的一般概念?
  2. 是否有一种公认的方式,仅使用仿函数代数,来移动可折叠/可遍历仿函数的内容? (注意,上面的计算可能用英语描述,"从右边移动一个值,并将左边的值加回到新的第一个值。")

4 个答案:

答案 0 :(得分:5)

您可以使用Foldable中的FirstLast幺半群找到Data.Monoid的第一个或最后一个元素。

foldMap (Last . Just)  :: Foldable t => t a -> Last a
foldMap (First . Just) :: Foldable t => t a -> First a

所有Foldable都可转换为列表,因此您可以找到列表的头部和尾部,您可以为任何Foldable执行此操作。

toList = foldr (:) [] :: Foldable t => t a -> [a]

也就是说,尾部将有一个列表类型,而不是Foldable的列表类型(除非它是一个列表)。这最终是因为并非所有Foldable都可以实现uncons。例如:

data Pair a = Pair a a

这是Foldable,但您无法使用Pair代表Pair的尾部。

答案 1 :(得分:4)

问题的第一部分(将结构的第一个值与一个事物相结合,其余部分相同)可以使用Traversable以简单的方式完成。我们将使用State,使用我们要应用的功能启动它,并立即将其修改为id

onlyOnHead :: Traversable t => (a -> a) -> t a -> t a
onlyOnHead f xs = evalState (traverse go xs) f where
    go x = do
        fCurrent <- get
        put id
        return (fCurrent x)

您可以使用类似的想法轮换元素:我们将旋转列表,并将其作为要从中绘制元素的State中的内容。

rotate :: Traversable t => t a -> t a
rotate xs = evalState (traverse go xs) (rotateList (toList xs)) where
    rotateList [] = []
    rotateList vs = tail vs ++ [head vs]

    go _ = do
        v:vs <- get
        put vs
        return v

答案 2 :(得分:1)

要旋转,您不需要任何丑陋的部分功能。这个奇怪的optionPressed会起到作用。

Applicative

您可以轮播data Foo a t where Cons :: (a -> q -> t) -> a -> Foo a q -> Foo a t Nil :: t -> Foo a t instance Functor (Foo a) where fmap f (Cons g x xs) = Cons (\p q -> f (g p q)) x xs fmap f (Nil q) = Nil (f q) instance Applicative (Foo a) where pure = Nil liftA2 f (Nil t) ys = f t <$> ys liftA2 f (Cons g x xs) ys = Cons (\a (q,b) -> f (g a q) b) x (liftA2 (,) xs ys)

Foo

运行一个以获得结果:

rot :: Foo a t -> Foo a t
rot n@(Nil _) = n
rot (Cons g0 a0 as0) = go g0 a0 as0
  where
    go :: (a -> q -> t) -> a -> Foo a q -> Foo a t
    go g a n@(Nil _) = Cons g a n
    go g a (Cons h y ys) = Cons g y (go h a ys)

全部放在一起,

runFoo :: Foo a t -> t
runFoo (Nil t) = t
runFoo (Cons g x xs) = g x (runFoo xs)

然后rotate :: Traversable t => t a -> t a rotate = runFoo . rot . traverse (\a -> Cons const a (Nil ()))

答案 3 :(得分:0)

感谢所有回复的人!

请注意Conal Elliott's shaped-types library在这方面也有一些有用的机制。