如何实现这个Traversable使用模式?

时间:2013-10-10 22:47:14

标签: haskell traversal applicative

使用Data.Traversable时,我经常需要一些代码,例如

import Control.Applicative (Applicative,(<*>),pure)
import Data.Traversable (Traversable,traverse,sequenceA)
import Control.Monad.State (state,runState)

traverseF :: Traversable t => ((a,s) -> (b,s)) -> (t a, s) -> (t b, s)
traverseF f (t,s) = runState (traverse (state.curry f) t) s

遍历结构并构建一个由某个州驱动的新结构。我注意到了类型签名模式,并相信它可以概括为

fmapInner :: (Applicative f,Traversable t) => (f a -> f b) -> f (t a) -> f (t b)
fmapInner f t = ???

但我无法仅使用traversesequenceAfmap<*>pure来实现此功能。也许我需要更强的类型约束?我绝对需要Monad吗?

更新

具体来说,我想知道我是否可以为适用于任何fmapInner的{​​{1}}定义f以及应用直觉的一些法律(我不知道法律应该是什么是的,这是Traversable tf的意思吗?因为,对于Monad,实现是微不足道的:

Monad

更新

感谢您的出色回答。我发现我的--Monad m implies Applicative m but we still -- have to say it unless we use mapM instead fmapInner :: (Monad m,Traversable t) => (m a -> m b) -> m (t a) -> m (t b) fmapInner f t = t >>= Data.Traversable.mapM (\a -> f (return a)) 只是

traverseF

不显式使用Monad.State并且所有对都被翻转。以前我虽然是import Data.Traversable (mapAccumL) traverseF1 :: Traversable t => ((a, b) -> (a, c)) -> (a, t b) -> (a, t c) traverseF1 =uncurry.mapAccumL.curry ,但实际上mapAccumR就像mapAccumL一样。

1 个答案:

答案 0 :(得分:2)

我现在已经说服自己这是不可能的。这就是原因,

 tF ::(Applicative f, Traversable t) => (f a -> f b) -> f (t a) -> f (t b)

所以我们有这个副作用计算返回t a,我们想用它来确定会发生什么副作用。换句话说,类型t a的值将决定在我们应用traverse时会发生什么副作用。

然而,应用类型类无法实现这一点。我们可以动态选择值,但计算外的副作用是静态的。看看我的意思,

pure :: a -> f a -- No side effects
(<*>) :: f (a -> b) -> f a -> f b -- The side effects of `f a` can't
                                  -- decide based on `f (a -> b)`.

现在有两种可以想象的方法来确定依赖于先前值的副作用,

smash :: f (f a) -> f a

因为那时我们可以简单地做

smash $ (f :: a -> f a) <$> (fa :: f a) :: f a

现在您的功能变为

traverseF f t = smash $ traverse (f . pure) <$> t

或者我们可以

bind :: m a -> (a -> m b) -> m b -- and it's obvious how `a -> m b`
                                 -- can choose side effects.

traverseF f t = bind t (traverse $ f . pure)

但它们分别是join>>=,并且是Monad类型类的成员。所以是的,你需要一个monad。 :(

此外,使用monad约束的函数的一个很好的,无点的实现是

traverseM = (=<<) . mapM . (.return)

编辑,

我认为值得注意的是

traverseF :: (Applicative f,Traversable t) => (f a -> f b) -> t a -> f (t a)
traverseF = traverse . (.pure)