Haskell 我可以在这里使用bind/fmap吗 loadTexture::String->IO(字符串GL.GLuint中的任意一个) loadTexture filename=do p IO(任意字符串GL.GLuint) oglLoadImg(左e)=返回$Left e oglLoadImg(右png)=do ... 我需要在这里做些事情
上面的代码看起来非常臃肿和肮脏。我能做些什么来简化它呢?这个怎么样Haskell 我可以在这里使用bind/fmap吗 loadTexture::String->IO(字符串GL.GLuint中的任意一个) loadTexture filename=do p IO(任意字符串GL.GLuint) oglLoadImg(左e)=返回$Left e oglLoadImg(右png)=do ... 我需要在这里做些事情,haskell,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 S
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
loadTexture::String->IO(字符串GL.GLuint中的任意一个)
loadTexture文件名=任意(return.Left)oglLoadImg=IO(任意字符串GL.GLuint)
oglLoadImg png=do--IO这里的东西
(我对
other(return.Left)
位不太满意,不知道是否可以用某种lift
咒语来代替它。)使用Data.Traversable.Traversable
的一个实例,用于other
然后mapM
。例如:
loadTexture :: String -> IO (Either String GL.GLuint)
loadTexture filename = either (return . Left) oglLoadImg =<< PNG.loadPNGFile filename
where
oglLoadImg :: PNG.PNGImage -> IO (Either String GL.GLuint)
oglLoadImg png = do -- IO stuff in here
本质上,您需要一个emonad和IOmonad的组合。这就是我们的目的 在本例中,您可以使用它将使用
或的错误处理添加到基础monad,在本例中为IO
loadTexture :: String -> IO (Either String GL.GLuint)
loadTexture filename = do
p <- PNG.loadPNGFile filename
forM p $ \p -> do
-- Whatever needs to be done
-- continue here.
这样就保留了旧的界面,尽管对函数使用error
可能会更好,并在main
函数中调用runerror
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
-- [...]
在进行风格重构之前,让我们后退一步,想想你的代码在这里所做的语义。
您有一个IO
操作,该操作生成类型为或字符串PNG.PNGImage
,其中左侧的案例是一条错误消息。您认为应该在存在右
案例时对其进行处理,同时保留错误消息。想一想这个复合运算可能是什么样子的,如果你把它压缩成一个单一的,广义的组合运算:
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
-- [...]
现在我们已经合并了概念合成操作,我们可以开始更有效地压缩特定操作。将do
块折叠为一元函数应用程序是一个良好的开端:
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
loadTexture::String->ErrorT String IO GL.GLuint
loadTexture文件名=提升。oglLoadImg=IO GL.GLuint
oglLoadImg png=do putStrLn“…我需要在这里处理IO内容”
返回0
根据您在oglLoadImg
中所做的工作,您可能可以做得更多。是的,lift
在这里是合适的。有关详细信息,请参见哈马尔和我几乎同时给出的答案@tm1rbrt I将类型签名汇总起来;希望现在能修好。但是hammer和C.A.McCann的答案更好。是否可以使用return(PNG.loadPNGFile filename)
代替error(PNG.loadPNGFile filename)
?
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
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
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