6 haskell
loadTexture :: String -> IO (Either String GL.GLuint)
loadTexture filename = do
p <- PNG.loadPNGFile filename
oglLoadImg p
where
oglLoadImg :: (Either String PNG.PNGImage) -> IO (Either String GL.GLuint)
oglLoadImg (Left e) = return $ Left e
oglLoadImg (Right png) = do
... I need todo IO stuff in here
Run Code Online (Sandbox Code Playgroud)
上面的代码看起来真的很臃肿和讨厌.我该怎么做才能让它变得更简单?
ham*_*mar 14
你基本上想要Either emonad和IOmonad 的组合.这就是monad变形金刚的用途!
在这种情况下,你可以使用的ErrorT单子转换它增加了使用错误处理Either,以潜在的单子,在这种情况下IO.
import Control.Monad.Error
loadTexture :: String -> IO (Either String GL.GLuint)
loadTexture filename = runErrorT $ ErrorT (PNG.loadPNGFile filename) >>= oglLoadImg
where
oglLoadImg :: PNG.PNGImage -> ErrorT String IO GL.GLuint
oglLoadImg png = do
-- [...]
Run Code Online (Sandbox Code Playgroud)
这保留了旧的界面,虽然它可能更好地ErrorT用于你的功能,并runErrorT在你的main功能中调用.
loadTexture :: String -> ErrorT String IO GL.GLuint
loadTexture filename = ErrorT (PNG.loadPNGFile filename) >>= oglLoadImg
where
oglLoadImg :: PNG.PNGImage -> ErrorT String IO GL.GLuint
oglLoadImg png = do
-- [...]
Run Code Online (Sandbox Code Playgroud)
Monad变形金刚可能需要一些习惯,但它们非常有用.
C. *_*ann 12
在进行风格重构之前,让我们退后一步,考虑一下代码在这里所做的事情的语义.
你有一个IO产生类型的动作Either String PNG.PNGImage,其中Left大小写是一个错误信息.您认为想要在Right案例存在的情况下执行某些操作,同时保留错误消息.想想这个复合操作可能是什么样子,如果你把它压缩成一个通用的组合器:
doIOWithError :: IO (Either String a) -> (a -> IO b) -> IO (Either String b)
doIOWithError x f = do x' <- x
case x' of
Left err -> return (Left err)
Right y -> f y
Run Code Online (Sandbox Code Playgroud)
虽然这可能是有用的,但您可能已经注意到它的类型签名看起来很可疑(>>=) :: (Monad m) => m a -> (a -> m b) -> m b.事实上,如果我们概括了一步,允许产生错误以及功能,我们有确切的类型(>>=),其中m a变IO (Either String a).不幸的是,你不能把它作为一个Monad实例,因为你不能直接将类型构造函数粘合在一起.
你可以做的是将它包装在一个newtype别名中,事实上事实证明有人已经拥有:这只是Either用作monad变换器,所以我们想要ErrorT String IO.重写你要使用的函数,这样就可以了:
loadTexture :: String -> ErrorT String IO GL.GLuint
loadTexture filename = do
p <- ErrorT $ loadPNGFile filename
lift $ oglLoadImg p
where
oglLoadImg :: PNG.PNGImage -> IO GL.GLuint
oglLoadImg png = do putStrLn "...I need todo IO stuff in here"
return 0
Run Code Online (Sandbox Code Playgroud)
现在我们已经合并了概念复合操作,我们可以开始更有效地压缩特定操作.将do块折叠到monadic函数应用程序是一个好的开始:
loadTexture :: String -> ErrorT String IO GL.GLuint
loadTexture filename = lift . oglLoadImg =<< ErrorT (loadPNGFile filename)
where
oglLoadImg :: PNG.PNGImage -> IO GL.GLuint
oglLoadImg png = do putStrLn "...I need todo IO stuff in here"
return 0
Run Code Online (Sandbox Code Playgroud)
根据你所做的事情oglLoadImg,你可能会做得更多.