
For a small webapi binding I try to implement a session like monad by building a stack including BrowserAction from Network.Browser module as following: newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState) I would like the RBAction to implement MonadIO as well, but fight with the liftIO function for hours now, without success. Any idea how the implementation of liftIO could look like? Thanks for help, Adam

Assuming you have "ioAction :: IO x -> BrowserAction x" (from http://homepages.paradise.net.nz/warrickg/haskell/http/#browser) you can do:
instance MonadIO BrowserAction where liftIO = ioAction
Then you can derive MonadIO with GHC's newtype deriving. Alternatively, you can implement it directly on your type:
instance MonadIO RBAction where liftIO = RBAction . lift . lift . ioAction
This just inserts the proper number of "lift"s to bring the
BrowserAction through your monad transformer stack, and then wraps it
with your newtype constructor.
On Thu, Apr 10, 2008 at 7:50 AM, Adam Smyczek
For a small webapi binding I try to implement a session like monad by building a stack including BrowserAction from Network.Browser module as following:
newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState)
I would like the RBAction to implement MonadIO as well, but fight with the liftIO function for hours now, without success. Any idea how the implementation of liftIO could look like?
Thanks for help, Adam
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Thu, Apr 10, 2008 at 9:50 AM, Adam Smyczek
For a small webapi binding I try to implement a session like monad by building a stack including BrowserAction from Network.Browser module as following:
newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState)
I would like the RBAction to implement MonadIO as well, but fight with the liftIO function for hours now, without success. Any idea how the implementation of liftIO could look like?
Adam, The following worked for me: +++++ {-# LANGUAGE GeneralizedNewtypeDeriving #-} import Control.Monad.Trans import Control.Monad.State import Control.Monad.Error type BrowserAction = IO -- or something else which is in MondaIO data RBState = RBState newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState) instance MonadIO RBAction where liftIO = RBAction . liftIO +++++ Because everything inside the newtype wrapper already supports MondIO, you just need to call the version of liftIO underneath the newtype wrapper. Is that clear at all? -Antoine

On Thu, Apr 10, 2008 at 12:44 PM, Antoine Latter
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad.Trans import Control.Monad.State import Control.Monad.Error
type BrowserAction = IO -- or something else which is in MondaIO data RBState = RBState
newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState, MonadIO)
D'oh! Deriving MonadIO for the newtype should work, so long as BrowserAction is in the MonadIO class. -Antoine

On Thu, Apr 10, 2008 at 7:50 AM, Adam Smyczek
For a small webapi binding I try to implement a session like monad by building a stack including BrowserAction from Network.Browser module as following:
newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState)
I would like the RBAction to implement MonadIO as well, but fight with the liftIO function for hours now, without success. Any idea how the implementation of liftIO could look like?
In order to make a stack of monads an instance of MonadIO, you need to be able to run IO actions in the innermost monad (BrowserAction). Ideally, that monad itself would be an instance of MonadIO; maybe the authors of HTTP didn't do so because it would add a dependency on the mtl package (which defines the MonadIO class). Luckily, though, Network.Browser provides ioAction :: IO a -> BrowserAction a which is exactly what you need to write the MonadIO instances yourself. -- Solution #1 --
instance MonadIO BrowserAction where liftIO = ioAction
Then you can add MonadIO to the deriving clause for RBAction. -- Solution #2 --
instance MonadIO RBAction where liftIO = RBAction . lift . lift . ioAction
This pulls IO actions manually through the stack of transformers. It might be better because if the HTTP package ever provided its own instance of MonadIO BrowserAction, that would conflict with #1. Hope that helps, -Judah

Thanks a lot for all explanations! It looks like 'ioAction' is the key to the solution and if the Browser module did not provide/expose this function, no IO actions could be run inside the BrowserAction monad? If yes, is this a general concept/pattern how to hide functionality of a underlying monad, in this case hide IO entirely? Adam On Apr 10, 2008, at 11:02 AM, Judah Jacobson wrote:
On Thu, Apr 10, 2008 at 7:50 AM, Adam Smyczek
wrote: For a small webapi binding I try to implement a session like monad by building a stack including BrowserAction from Network.Browser module as following:
newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState)
I would like the RBAction to implement MonadIO as well, but fight with the liftIO function for hours now, without success. Any idea how the implementation of liftIO could look like?
In order to make a stack of monads an instance of MonadIO, you need to be able to run IO actions in the innermost monad (BrowserAction). Ideally, that monad itself would be an instance of MonadIO; maybe the authors of HTTP didn't do so because it would add a dependency on the mtl package (which defines the MonadIO class).
Luckily, though, Network.Browser provides ioAction :: IO a -> BrowserAction a which is exactly what you need to write the MonadIO instances yourself.
-- Solution #1 --
instance MonadIO BrowserAction where liftIO = ioAction
Then you can add MonadIO to the deriving clause for RBAction.
-- Solution #2 --
instance MonadIO RBAction where liftIO = RBAction . lift . lift . ioAction
This pulls IO actions manually through the stack of transformers. It might be better because if the HTTP package ever provided its own instance of MonadIO BrowserAction, that would conflict with #1.
Hope that helps, -Judah

On Thu, 2008-04-10 at 11:53 -0700, Adam Smyczek wrote:
Thanks a lot for all explanations!
It looks like 'ioAction' is the key to the solution and if the Browser module did not provide/expose this function, no IO actions could be run inside the BrowserAction monad?
If yes, is this a general concept/pattern how to hide functionality of a underlying monad, in this case hide IO entirely?
Yes, only there is nothing particular to monads. This would be just utilizing a standard abstract data type.

On 4/10/08, Adam Smyczek
If yes, is this a general concept/pattern how to hide functionality of a underlying monad, in this case hide IO entirely?
Yes, that's correct, although with IO you can't hide it entirely; eventually you need a way to actually run the computation, and if that's built on IO there's no way to do that without at least a way to get -back- to the IO Monad. On the other hand, you can use this to encapsulate "sandboxed" computations:
module Console (Console, execConsole, consoleGetLine, consolePutLine) where
newtype Console a = MkConsole { execConsole :: IO a } deriving (Monad, Functor)
consoleGetLine :: Console String consoleGetLine = MkConsole getLine
consolePutLine :: String -> Console () consolePutLine = MkConsole . putStrLn
MkConsole is a private constructor not exported from this module, so the only way to construct one is via the operations we provide and the monad/functor operations. So we can prove that these operations never do any network access, or file I/O, or weird pointer access. Of course, with unsafeCoerce# and/or unsafePerformIO, client code can break either/both of these claims:
runConsole :: Console a -> a runConsole = unsafePerformIO . execConsole
instance MonadIO Console where liftIO = unsafeCoerce# -- works because newtype is guaranteed not to change -- the runtime representation
-- ryan

On Thu, Apr 10, 2008 at 2:50 PM, Adam Smyczek
For a small webapi binding I try to implement a session like monad by building a stack including BrowserAction from Network.Browser module as following:
newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState)
I would like the RBAction to implement MonadIO as well, but fight with the liftIO function for hours now, without success. Any idea how the implementation of liftIO could look like?
I suspect BrowserAction does not implement MonadIO (lest you could just put MonadIO in the deriving clause). So, that depends on how BrowserAction is implemented. What package is Network.Browser in? Luke

On Apr 10, 2008, at 12:42 PM, Luke Palmer wrote:
On Thu, Apr 10, 2008 at 2:50 PM, Adam Smyczek
wrote: For a small webapi binding I try to implement a session like monad by building a stack including BrowserAction from Network.Browser module as following:
newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState)
I would like the RBAction to implement MonadIO as well, but fight with the liftIO function for hours now, without success. Any idea how the implementation of liftIO could look like?
I suspect BrowserAction does not implement MonadIO (lest you could just put MonadIO in the deriving clause). So, that depends on how BrowserAction is implemented. What package is Network.Browser in?
It's correct, BrowserAction does not implement MonadIO. This are the small things that confuse beginners like me :) Package http: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/HTTP-3001.0.4
Luke

Adam Smyczek wrote:
For a small webapi binding I try to implement a session like monad by building a stack including BrowserAction from Network.Browser module as following:
newtype RBAction a = RBAction { exec :: ErrorT String (StateT RBState BrowserAction) a } deriving (Functor, Monad, MonadState RBState)
I would like the RBAction to implement MonadIO as well, but fight with the liftIO function for hours now, without success. Any idea how the implementation of liftIO could look like?
I would add instance MonadIO BrowserAction where liftIO = ioAction to hook Network.Browser into the monad transformer framework. I don't know why this instance is missing from Network.Browser. Maybe to avoid a dependency on the mtl? Anyway, with this instance, MonadIO can simply be derived for RBAction: newtype RBAction a = ... deriving (MonadIO, ...) Tillmann
participants (7)
-
Adam Smyczek
-
Antoine Latter
-
Derek Elkins
-
Judah Jacobson
-
Luke Palmer
-
Ryan Ingram
-
Tillmann Rendel