Meaning code like this isn’t possible using just applicatives:
getFriendsUsernames :: Id -> DB [Username] getFriendsUsernames user = do ids <- getFriends user traverse getUsername ids
It is possible in pretty much the same way as they do with their free profunctors (and in the same way as I did with FRP in the link I included in my previous comment):
data DB a b where
GetUsernames :: DB (Set Id) -> DB (Map Id Username)
GetFriends :: DB Id -> DB (Set Id)
Pure :: a -> DB a
Ap :: DB (a -> b) -> DB a -> DB b
getFriendsUsernames :: Id -> DB [Username]
getFriendsUsernames user =
Ap (Pure toList) (GetFriends (GetUsernames (Pure user)))
I think there may be a drawback of using applicatives like this instead of arrows/profunctors. In particular, I think arrows could support sharing better. But I haven’t seen a concrete example of that yet.
Edit: Actually, it seems the traverse' function may be a problem for applicatives, because the signature would have to include a higher order function: Traverse' :: Traversable f => (DB a -> DB b) -> DB (f a) -> DB (f b), but then you wouldn’t be able to inspect that function that we’re traversing with. Although, it still remains to be shown that this is actually a problem.