Delimiting the IO monad

别等时光非礼了梦想. 提交于 2019-11-30 11:06:40

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

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.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!