So I accidentally a profunctors tutorial…

View 6 previous comments

- Yes that is a little odd, thanks for pointing it out.

If you don't care about the index (yet want to preserve whether a function argument accepts an index), you can write something that takes a Profunctor p => (a `p` b) -> ... instead of instantiating it to either (a -> b) -> ... or Indexed i a b -> ... This point I appear to have omitted. (I can't remember if I talked about it, and I hate watching myself on video so I'm not going to find out.)

However if you want to supply an index without regard for whether the function argument actually wants it, (as mapIndexable does) you use the Indexable class constraint as shown in the article.

Hope that clears things up. I'll try to come up with an example to illustrate the first point, but if you happen across a good one, please mention it here and I'll amend the article.Oct 15, 2013 - Than you for comment Liyang.

> If you don't care about the index (yet want to preserve whether > a function argument accepts an index), you can write

> something that takes a Profunctor p => (a `p` b) -> ... instead of > instantiating it to either (a -> b) -> ... or Indexed i a b -> ...

I still don't quite get this one, what the meaning of "don't care" and "preserve" is in this context.

If you could come up with an example it would be very appreciated.Oct 17, 2013 - Daniel: indexed lenses and traversals in the lens library use a class substantially identical to the Indexable from liyang hu's post here.

class Profunctor p => Indexable i p where

indexed :: p a b -> i -> a -> b

instance Indexable i (->) where

indexed = const

newtype Indexed i a b = Indexed (i -> a -> b)

instance (i ~ j) => Indexable i (Indexed j) where

indexed = Indexed

This seems like mental masturbation for a while, but consider that say, many data types like Map provide you with something like

traverseWithKey :: Applicative f => (k -> a -> f b) -> Map k a -> f (Map k b)

That doesn't fit the p a (f b) -> q s (f t) form expected by lens, but we can write

itraverse :: (Applicative f, Indexable k p) => p a (f b) -> Map k a -> f (Map k b)

itraverse f = traverseWithKey (indexed f)

now when p = (->) then itraverse devolves to having the same power and type signature as traverse, but when you pick p = Indexed i it has all the power of traverseWithKey!

This means you can write one traversal that works with all the standard lens combinators that don't care about indices, but which will magically upgrade to give you access to the index when you need it for others.

This lets us avoid duplicating the entire API when it comes to adding indexed lenses and the like to the mix and dealing with expensive ugly explicit coercions between them, which aren't in the style of the lens library.

Indices are very useful for giving users access to some 'immutable metadata' about where they are in a traversal.Oct 17, 2013 - Thanks Edward for your explanations.

I think that I already got the point of Indexable, but still don't get what the

Profunctor constraint in your version of Indexable adds.

Liyangs version of Indexable didn't had the Profunctor constrain, but still

did abstract over traverse and traverseWithKey.Oct 17, 2013 - The profunctor constraint isn't strictly necessary for the indexing trick. however, the two instances we care about are profunctors and it removes noise in a lot of signatures involving this data type.Oct 17, 2013
- Thanks Edward, that was exactly the missing peace of information.

So strictly speaking, the indexing trick isn't an example for the usage of a Profunctor.Oct 17, 2013