Haskell 如何实现这种可遍历的使用模式?
当使用Data.Traversable时,我经常需要一些类似的代码Haskell 如何实现这种可遍历的使用模式?,haskell,traversal,applicative,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)) -&g
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
但是我无法通过遍历
,序列a
,fmap
,
和纯
来实现这一点。也许我需要更强的类型类约束?我真的需要一个Monad
吗
更新
具体地说,我想知道,我是否可以为一个f
定义fmapInner
,它适用于任何可遍历的t
,以及一些直觉法则(我还不知道这些法则应该是什么),这是否意味着f
东西是一个单子?因为,对于Monad
s,实现非常简单:
--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
只是
import Data.Traversable (mapAccumL)
traverseF1 :: Traversable t => ((a, b) -> (a, c)) -> (a, t b) -> (a, t c)
traverseF1 =uncurry.mapAccumL.curry
不显式使用Monad.State并翻转所有对。以前我认为它是mapacumr
,但实际上是mapacuml
像traverseF
一样工作,现在我确信这是不可能的。这就是为什么
tF ::(Applicative f, Traversable t) => (f a -> f b) -> f (t a) -> f (t b)
因此,我们有一个副作用计算,返回ta
,我们想用它来确定发生了什么副作用。换句话说,typeta
的值将决定当我们应用遍历时会发生什么副作用
但是,对于应用程序类型类,这是不可能的。我们可以动态地选择值,但out计算的副作用是静态的。要明白我的意思
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 :: a -> f a) <$> (fa :: f a) :: f a
及
但它们分别是join
和>=
,是Monad
typeclass的成员。所以,是的,你需要一个单子(
此外,还提供了一个带有monad约束的函数的漂亮的无点实现
traverseM = (=<<) . mapM . (.return)
您在fmapInner
中要求的类型在我看来与traverseF
的类型大不相同。也许您更喜欢foo::(a->fb)->(ta->f(tb))
或类似的东西,在这种情况下,它只是遍历
。然后f
可以专门处理状态s
,基本上给你提供traverseF
的类型。在重读时,我确信我并不真正理解你的要求。所以请忽略我之前的评论,但我会离开我明白你的意思,我也明白你刚才指出的事情。我已经知道我可以编写像你这样的函数;但是,在我看来,定义一个可重用函数fa->fb
,然后用它将af(ta)
转换成f(tb)似乎很自然,除非我假设f
是一个Monad
,否则似乎没有好的方法可以这样做。你认为(,)s
的应用实例是什么(我们必须把s
放在第一位,因为我们没有为类型定义翻转
)。我不是说(,)s)
目前是Applicative
的一个实例,但是如果您愿意,我们可以定义一个newtype
来实例化它。重点是类型签名模式,而不是具体的类型。我试着按照您对pure
、
和bind
的评论,但我认为我不太了解它们。您能理解吗给他们更多解释一下?
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)
traverseM = (=<<) . mapM . (.return)
traverseF :: (Applicative f,Traversable t) => (f a -> f b) -> t a -> f (t a)
traverseF = traverse . (.pure)