5

Traversable is in a sense the class of containers whose structure has a “path” (that can correspond to a list), the elements on which can be modified without dissolving the structure. Hence

zipTrav :: Traversable t => t a -> [b] -> Maybe (t (a,b))
zipTrav = evalStateT . traverse zp
 where zp a = do
           bs <- get
           case bs of
              [] -> lift Nothing
              (b:bs') -> put bs' >> return (a,b)

However, that list-state traversal seems a bit hackish and likely not the most efficient way to do it. I'd suppose there would be a standard function that accomplished the above or a more general task, but I can't figure out what it would be.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319

1 Answers1

5

What about mapAccumL/mapAccumR?

tzipWith :: Traversable t => (a -> b -> c) -> [a] -> t b -> Maybe (t c)
tzipWith f xs = sequenceA . snd . mapAccumL pair xs
    where pair [] y = ([], Nothing)
          pair (x:xs) y = (xs, Just (f x y))

tzip :: Traversable t => [a] -> t b -> Maybe (t (a, b))
tzip = tzipWith (,)

ghci> tzip [1..] [4,5,6]
Just [(1,4),(2,5),(3,6)]

ghci> tzip [1,2] [4,5,6]
Nothing

On the question of efficiency - under the hood the mapAccum functions use the state monad, so all I've really done is capture the imperative part of your code in a higher-order function. I wouldn't expect this code to perform better than yours. But I don't think you can do much better than the State monad (or ST), given only Traversable t.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Benjamin Hodgson
  • 42,952
  • 15
  • 108
  • 157
  • @WillNess Nah, you just need to check whether there's anything left in the input list after you've finished traversing. I'll update my code – Benjamin Hodgson Jan 11 '17 at 17:49
  • @WillNess I think the questioneer wants to return `Nothing` if there aren't enough items in the list to fill up the `Traversable`. The idea is to keep the input `Traversable`'s shape while pairing up each of the values – Benjamin Hodgson Jan 11 '17 at 17:58
  • ah, this makes sense. this means your previous version was indeed correct. :) I've restored your previous version, and added a clarifying example. sorry for this. – Will Ness Jan 11 '17 at 18:23
  • Perhaps "use the state monad" should be changed to "use the state applicative", to emphasise the applicative instance of state is enough. – duplode Jan 18 '17 at 17:07
  • @duplode It's a tricky one. On the one hand, your suggestion uses more precise terminology, but on the other hand the term _state monad_ is a more widely-recognised name for the concept (despite the fact that it's also an applicative functor), plus the phrase "the state applicative" doesn't trip off the tongue as easily as "the state monad". It's my answer so I'll use whatever word I want! – Benjamin Hodgson Jan 18 '17 at 22:54
  • That's fine; there is indeed a decent case for either option. I just made the suggestion because I'm quite fond of `mapAccumL` as an example of the applicative instance of `State`. – duplode Jan 18 '17 at 23:24