9

I'm looking to filter a traversal, then select the last element to use with over.

e.g. something like this (but which will actually compile):

[1,2,3,4] & traverse . filtered even . _last +~ 10
> [1,2,3,14]

Any ideas?

P.S. I'm aware that filtered is only valid when not affecting the number of elements in the traversal.

The actual use case I'm performing is to select only the lowest level of a recursive uniplate traversal that matches some predicate; if you have other ideas of how to do this I'd love to hear them!

Chris Penner
  • 1,881
  • 11
  • 15
  • I had a crack at doing this by traversing in the reverse state monad transformer, but that gives you a signature of `MonadFix m => Traversal' s a -> LensLike' m s a` which is not terribly useful - you can only put values into it, not get them out, because `Const s` is not a `Monad`. An alternative approach would be to get the length of the collection by folding and then count statefully up to the last index - that'd give you a `Monad` constraint instead of `MonadFix` at the expense of some efficiency. Going via a concrete list, as in @Gurkenglas's answer, is probably your best bet – Benjamin Hodgson Jun 19 '17 at 12:57

2 Answers2

2
[1,2,3,4] & partsOf (traverse . filtered even) . _last +~ 10
Gurkenglas
  • 2,317
  • 9
  • 17
2

This isn't really an answer, just a follow-up to @Gurkenglas that's too big for a comment. Note that @Gurkenglas's answer:

let t = partsOf (traverse . filtered even) . _last

may look like a Traversal, but it isn't, even if you maintain the element count, because it violates the second Traversal law (for obvious reasons):

let f = Identity . succ
[1,2,3,4] & fmap (t f) . t f  -- yields [1,3,3,5] effectively
[1,2,3,4] & getCompose . t (Compose . fmap f . f)
                              -- yields [1,2,3,6] effectively

For it to be a traversal, you have to maintain both the element count and the filtering property as invariants.

Whether this will matter for your application, I don't know, but just be aware that partsOf comes with these sorts of caveats, and the documentation misleadingly suggests that the result of partsOf will be a Lens if you maintain the element count. (That's true for partsOf each, the example given in the documentation, but not in general.)

K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71