Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delimiting the IO monad

Tags:

io

haskell

monads

It's nice to know (in Safe Haskell, at least) from the signature whether or not something performs IO actions, but IO encompasses a lot of different things - putStr, database access, removing and writing to files, IORefs, etc.

If I'm using the type signatures as a security measure when running arbitrary code, it might be the case that I'm willing to accept some IO actions - putStr and the ilk, for instance - but not others.

Is there a way to define a restricted version of the IO monad, with only a subset of the normal IO actions? If so, an example (with putStr, for instance) would be very welcome!

like image 657
user2141650 Avatar asked Dec 02 '13 00:12

user2141650


People also ask

What is an IO Monad?

So, What is an IO Monad? IO Monad is simply a Monad which: Allows you to safely manipulate effects. Transform the effects into data and further manipulate it before it actually gets evaluated.

How is Haskell IO pure?

Haskell is a pure language Being pure means that the result of any function call is fully determined by its arguments. Procedural entities like rand() or getchar() in C, which return different results on each call, are simply impossible to write in Haskell.

What is IO Haskell?

IO is the way how Haskell differentiates between code that is referentially transparent and code that is not. IO a is the type of an IO action that returns an a . You can think of an IO action as a piece of code with some effect on the real world that waits to get executed.

How is Haskell IO implemented?

It's implemented using unsafeInterleaveIO , which does trickery behind the scenes to allow lazy I/O. It's not a good example of how IO is supposed to work.


2 Answers

As a follow up to my comment, you can implement it yourself with something like

class Monad io => Stdout io where
    putStr_ :: String -> io ()
    putStrLn_ :: String -> io ()
    print_ :: Show a => a -> io ()
    -- etc

instance Stdout IO where
    putStr_ = putStr
    putStrLn_ putStrLn
    print_ = print

myFunc :: Stdout io => io ()
myFunc = do
    val <- someAction
    print_ val
    let newVal = doSomething val
    print_ newVal

main :: IO ()
main = myFunc

This will have absolutely no runtime overhead, since GHC will optimize away those typeclasses to use only the IO monad, it's extensible, easy to write, and can be combined with monad transformes and the MonadIO class quite easily. If you have multiple class, such as a Stdin class with getLine_, getChar_, etc defined, you can even combine these typeclasses with

class (Stdout io, Stdin io) => StdOutIn io where

myFunc :: StdOutIn io => io ()
myFunc = do
    val <- getLine_
    putStrLn_ $ "Echo: " ++ val

main :: IO ()
main = myFunc
like image 152
bheklilr Avatar answered Oct 09 '22 11:10

bheklilr


Just define a newtype around IO a with a Monad instance, define wrapped versions of your pre-approved functions, and don't export the constructor, so that only the functions you wrapped can be used in the monad.

like image 35
jwodder Avatar answered Oct 09 '22 09:10

jwodder