首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >可能使用a、IO a和MaybeT IO a

可能使用a、IO a和MaybeT IO a
EN

Stack Overflow用户
提问于 2012-12-07 03:53:42
回答 2查看 2.8K关注 0票数 4

我正在编写一个即时响应风格的系统,其中包含一堆可能是a、IO a和MaybeT IO a的不同组合,并且有许多东西需要考虑。有些IO操作没有无效的输入(因此没有包装在MaybeT中),有些(并返回MaybeT IO a)有些不是IO操作,但可能会失败,所以可能会返回a,还有一些只是普通值,似乎我必须记住<$>, Just, fmap, MaybeT, lift, =<<,return的过度组合才能使所有内容都成为正确的类型。有没有更简单的方法来管理这一点,或者推断我需要使用什么函数来将我的值放在我需要的地方?或者,我只是希望随着时间的推移,我会变得更好?下面是我的例子:

代码语言:javascript
复制
getPiece :: Player -> Board -> MaybeT IO Piece
getPiece player@(Player pieces _ _ _) board = piece
    where
        promptString = displayToUserForPlayer player board ++ "\n" ++ (display player) ++ "\n" ++ "Enter piece number: "
        input :: MaybeT IO String
        input = lift $ prompt promptString
        index :: MaybeT IO Int
        index = MaybeT <$> return <$> ((fmap cvtFrom1indexedInt) . maybeRead) =<< input
        piece :: MaybeT IO Piece
        piece = MaybeT <$> return <$> maybeIndex pieces =<< index

getRotatedPiece :: Player -> Board -> MaybeT IO Piece
getRotatedPiece player@(Player pieces _ _ _) board = piece
    where
        promptString :: MaybeT IO String
        promptString = (++) <$> displayListString <*> restOfString
        input :: MaybeT IO String
        input = MaybeT <$> (fmap Just) <$> prompt =<< promptString
        index :: MaybeT IO Int
        index = MaybeT <$> return <$> ((fmap cvtFrom1indexedInt) . maybeRead) =<< input
        piece :: MaybeT IO Piece
        piece = MaybeT <$> return <$> maybeIndex pieces =<< index
        rotatedPieceList :: MaybeT IO [Piece]
        rotatedPieceList = rotations <$> getPiece player board
        displayListString :: MaybeT IO String
        displayListString = displayNumberedList <$> rotatedPieceList
        restOfString :: MaybeT IO String
        restOfString = MaybeT <$> return <$> Just $ "\nEnter rotation number:"

我必须说,我对简洁性的缺乏感到失望,即使我去掉了类型提示,我也可以用C#或python编写一个更短的函数来做同样的事情

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-12-07 04:57:27

因为您只提供了代码片段,所以我不能尝试重构它。然而,这就是我要做的:大多数monad都有一个对应的type类。它的原因正是您在这里需要的:当您使用monad转换器创建monad时,它将继承内部monad的操作(如果适用)。所以你可以忘记内部的monad,只在最终的monad中工作。

在您的例子中,您有MaybeT IO。它是MonadPlusMonadIO的实例。因此,您可以重构返回Maybe something的代码,以使用通用的MonadPlus实例,只需将Just替换为return,将Nothing替换为mzero。像这样:

代码语言:javascript
复制
-- before
checkNumber :: Int -> Maybe Int
checkNumber x | x > 0       = Just x
              | otherwise   = Nothing x
-- after
checkNumber :: MonadPlus m => Int -> m Int
checkNumber x | x > 0       = return x
              | otherwise   = mzero
-- or just: checkNumber = mfilter (> 0) . return

它可以与任何MonadPlus兼容,包括MaybeMaybeT IO

您还可以重构返回IO something的代码,以使用通用的MonadIO实例:

代码语言:javascript
复制
-- before
doSomeIO :: IO ()
doSomeIO = getLine >>= putStrLn
-- after
doSomeIO :: MonadIO m => m ()
doSomeIO = liftIO $ getLine >>= putStrLn

这样,你就可以忘记<$>/fmap/liftMJustMaybeT等。你只需要使用returnmzero,在某些地方使用liftIO

这也将帮助您创建更通用的代码。如果您后来意识到需要向monad堆栈添加一些东西,那么只要新的monad堆栈实现相同的类型类,现有的代码就不会中断。

票数 18
EN

Stack Overflow用户

发布于 2012-12-07 07:49:37

我给出了一个不那么雄心勃勃的答案。看一下您的代码,像getPiece这样的操作实际上并没有从特定的错误站点返回任何信息。如果你真的想要这些东西,你可以仅仅使用IO并将异常转换成Maybe值。我将一些示例代码与您的代码中引用的一些未定义的函数放在一起:

代码语言:javascript
复制
import Control.Exception (handle, IOException)

data Board = Board deriving (Show)
data Piece = Piece deriving (Show)
type Pieces = [Piece]
data Player = Player Pieces () () () deriving (Show)

prompt :: String -> IO String
prompt = undefined

cvtFrom1indexedInt :: Int -> Int
cvtFrom1indexedInt = undefined

maybeIndex :: Pieces -> Int -> Maybe Piece
maybeIndex = undefined

displayToUserForPlayer :: Player -> Board -> String
displayToUserForPlayer = undefined

display :: Player -> String
display = undefined

-- I used this when testing, to deal with the Prelude.undefined errors
--returnSilently :: SomeException -> IO (Maybe a)
returnSilently :: IOException -> IO (Maybe a)
returnSilently e = return Nothing

getPiece :: Player -> Board -> IO (Maybe Piece)
getPiece player@(Player pieces _ _ _) board = handle returnSilently $ do
    let promptString = displayToUserForPlayer player board ++ "\n" ++ (display player) ++ "\n" ++ "Enter piece number: "
    input <- prompt promptString
    let index = cvtFrom1indexedInt (read input)
    return (maybeIndex pieces index)

main = do
    maybePiece <- getPiece (Player [] () () ()) Board
    putStrLn ("Got piece: " ++ show maybePiece)

值得注意的是,我已经从MaybeT IO Piece转到了IO (Maybe Piece)。我没有使用fmaplift,而是使用do表示法来引用我的IO操作的中间结果。

关于你对C#或Python的评论,我希望这是你想要的那种更简单的答案。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13751430

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档