((a -> b) -> c) -> (a -> m b) -> m c

(Inspired by this[1] reddit thread.) When combining monadic and non-monadic code, I've often wished for a magical combinator of type (Monad m) => ((a -> b) -> c) -> (a -> m b) -> m c which would let me inject a monadic function into a pure one, then wrap the ultimate result to ensure that no nastiness escapes. Now, I'm pretty sure that writing such a combinator as-is is impossible. My question is, what restrictions/changes would be necessary for a similar combinator to be definable and well-behaved? (Obviously it would be impossible to ensure that the (m b)s get chained in any particular order, for order-sensitive monads, but it would be the caller's responsibility to ensure order-irrelevance in this case.) As a thought experiment, I imagined an implementation for IO using unsafePerformIO. This makes it easy to give the injected function the right type, and it's possible to use return to give the result the correct type too. Unfortunately, the semantics fail: if the injected function is used lazily, there's no way to ensure that the unsafe IO actions actually take place before the rest of the IO-based program, and it's possible for them to show up later than expected. I suspect that even in a strict language, lambda-abstractions would cause similar trouble. [1] http://programming.reddit.com/info/2n6eh/comments Stuart Cook

On Sun, 9 Sep 2007, Stuart Cook wrote:
(Inspired by this[1] reddit thread.)
When combining monadic and non-monadic code, I've often wished for a magical combinator of type
(Monad m) => ((a -> b) -> c) -> (a -> m b) -> m c
which would let me inject a monadic function into a pure one, then wrap the ultimate result to ensure that no nastiness escapes.
If the signature would be (Monad m) => ((a -> b) -> c) -> m (a -> b) -> m c it would be possible, and the implementation would be 'liftM'/'fmap'. In the Reader monad you can even convert (a -> m b) to m (a -> b)

On 9/9/07, Henning Thielemann
If the signature would be (Monad m) => ((a -> b) -> c) -> m (a -> b) -> m c it would be possible, and the implementation would be 'liftM'/'fmap'.
Thanks, that's the kind of insight I was looking for. Hmm. A key distinction between (a -> m b) and m (a -> b) is that in the latter, the monadic component can't depend on the value given to the function. One of the particular cases I had in mind was an (a -> IO b) that reads indexed values from a mutable data structure. It would be possible to convert this to IO (a -> b) if you knew ahead of time what reads you needed to do, without inspecting the "index" argument. Of course, you'd be unable to use the index to restrict the amount of reading necessary, which could be a problem for large structures. If you end up reading most of the values anyway, it might still be worth it.
In the Reader monad you can even convert (a -> m b) to m (a -> b)
Quite. In the ((->) r) monad, that's a conversion from (a -> r -> b) to (r -> a -> b), which is just "flip". I shall have to think about what properties are needed for a conversion from (a -> m b) to m (a -> b), and what other structures have those properties. Actually, all this talk of m (a -> b) reminds me of applicative functors, especially since you mentioned "fmap" (for vanilla functors) above. More food for thought ... Stuart

Henning Thielemann wrote:
On Sun, 9 Sep 2007, Stuart Cook wrote:
When combining monadic and non-monadic code, I've often wished for a magical combinator of type
(Monad m) => ((a -> b) -> c) -> (a -> m b) -> m c
which would let me inject a monadic function into a pure one, then wrap the ultimate result to ensure that no nastiness escapes.
If the signature would be (Monad m) => ((a -> b) -> c) -> m (a -> b) -> m c it would be possible, and the implementation would be 'liftM'/'fmap'.
In the Reader monad you can even convert (a -> m b) to m (a -> b)
The existence of a conversion convert :: (a -> m b) -> m (a -> b) between those two types for a given monad m is equivalent to the existence of magic :: ((a -> b) -> c) -> (a -> m b) -> m c since we have convert = magic id magic f g = return f `ap` convert g Regards, apfelmus
participants (3)
-
apfelmus
-
Henning Thielemann
-
Stuart Cook