
Sebastian Sylvan wrote:
Claus Reinke wrote:
mytransaction = do { x0 <- readTVar xvar0 x1 <- readTVar xvar1 : xn <- readTVar xvarn return $ foo x0 x1 .. xn }
ah, a concrete example. but isn't that the typical use case for ap?
mytransaction = foo `liftM` r xvar0 `ap` r xvar1 .. where r = readTVar
I really find it difficult to articulate why this isn't acceptable, because it seems so obvious to me! It's short yes, but I really don't think it's very clear... I have a hard time believing that anyone finds that natural.
I think it's entirely natural :) Applicative functors (Control.Applicative) are the pattern behind this. The notation may seem a little weird first, but in the end, `ap` is a kind of explicit function application and similar to $. With the notation from Control.Applicative, the line return foo `ap` r xvar0 `ap` r xvar1 `ap` ... reads pure foo <*> r xvar0 <*> r xvar1 <*> ... or foo <$> r xvar0 <*> r xvar1 <*> ... In other words, instead of using juxtaposition to apply an argument to a function, we use <*>. The type of `ap` is ap :: m (a -> b) -> m a -> m b so that it can be thought of as a generalized function application where the function is "under" a monad. The difference to $ is that <*> is left associative and allows for currying. I.e. <*> is like $ used in the following way ((foo $ x0) $ x1) $ x2 Note that you can even incorporate the TVar by defining your own generalized function application: apT :: STM (a -> b) -> TVar a -> STM b apT f x = f `ap` readTVar x Then, mytransaction reads mytransaction = return foo `apT` xvar0 `apT` xvar1 `apT` ... Regards, apfelmus