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