4

私は、Maybe a、IO a、MaybeT IO a のさまざまな組み合わせを含むプロンプト - レスポンス スタイルのシステムを作成していますが、考慮すべき点がたくさんあります。無効な入力がない (したがって MaybeT にラップされない) IO アクション、(MaybeT IO a を返す) IO アクションではないが失敗する可能性があるため、Maybe a を返すもの、および Maybe a を返すものがあります。それはただの単純な値であり、すべてを適切なタイプにするために、<$>, Just, fmap, MaybeT, lift, =<<,との過度の組み合わせを覚えておく必要があるように思われ始めています。returnこれを管理したり、必要な場所で値を取得するためにどの関数を使用する必要があるかを判断したりする簡単な方法はありますか? それとも、時間の経過とともにうまくいくことを祈る必要がありますか? これが私の例です:

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 で同じことを行うためのより短い関数を記述できる可能性があります。

4

2 に答える 2

18

コード フラグメントしか提供されていないため、リファクタリングを試みることはできません。ただし、これは私が行うことです。ほとんどのモナドには、対応する型クラスがあります。その理由はまさにここで必要なことです: モナドトランスフォーマーを使用してモナドを作成すると、内部モナドの操作を継承します (適切な場合)。そのため、内側のモナドを忘れて、最後のモナド内だけで作業できます。

あなたの場合、あなたは持っていMaybeT IOます。と のインスタンスMonadPlusですMonadIO。したがって、代わりに一般的なインスタンスでMaybe something動作するように戻るコードをリファクタリングできます。お気に入り:MonadPlusJustreturnNothingmzero

-- 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インスタンスで動作するように戻るコードをリファクタリングできます。

-- before
doSomeIO :: IO ()
doSomeIO = getLine >>= putStrLn
-- after
doSomeIO :: MonadIO m => m ()
doSomeIO = liftIO $ getLine >>= putStrLn

このようにして、<$>//などを忘れることができます。いくつかの場所で, and を使用するだけです。fmapliftMJustMaybeTreturnmzeroliftIO

これは、より一般的なコードを作成するのにも役立ちます。後でモナド スタックに何かを追加する必要があることに気付いた場合でも、新しいモナド スタックが同じ型クラスを実装している限り、既存のコードは壊れません。

于 2012-12-06T20:57:27.063 に答える
0

私からの野心的ではない答え。あなたのコードを見ると、あなたの操作getPieceは特定のエラー サイトからの情報を実際には返していません。Maybe本当に必要な場合は、おそらく IO を使用して例外を値に変換するだけで済むでしょう。あなたのコードで参照されているいくつかの未定義の関数と一緒にまとめたいくつかのサンプルコード:

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)fmaporを使用する代わりに、アクションの中間結果を参照するために記法をlift使用しました。doIO

C# や Python についてのコメントに続きますが、これがあなたが探していたより簡単な答えだったことを願っています。

于 2012-12-06T23:49:37.420 に答える