@ user2720372 が提案するよりも慣用的な解決策は、非モナディック コードをモナディック コードから分割することです。IO アクションはモナドのモナド関数IO
です。
ローカルでのみ必要な場合はgetFullPath
、ホーム ディレクトリをキャッシュするのが理にかなっています。
fullPath homePath s
| "~" `isPrefixOf` s = joinPath [homePath, tail s]
| otherwise = s
main = do
homePath <- getHomeDirectory
let getFullPath = fullPath homePath
print $ getFullPath "~/foo"
それでも完全なグローバルが必要な場合はgetFullPath
、次のように実装できます。
getFullPath p = do
homePath <- getHomeDirectory
return $ fullPath homePath p
fullPath
そして、それは維持してgetFullPath
分離するのに良いスタイルと考えられています.
また、そのような単純なケースではそもそも必要isPrefixOf
ありません。tail
fullPath homePath ('~' : t) = joinPath [homePath, t]
fullPath _ s = s
モノリシックだけが必要な場合はgetFullPath
、@ user2720372 のバリアントを単純化できます。
getFullPath s = do
homeDir <- getHomeDirectory
return $ case s of
('~' : t) -> joinPath [homeDir, t]
_ -> s
上記のコードは、誤った動作を保持するコードのリファクタリングにすぎないことに注意してください~
。最初のパス文字ではなく、最初のパス コンポーネントと比較する必要があります。splitPath
から使用System.FilePath
:
getFullPath s = do
homeDir <- getHomeDirectory
return $ case splitPath s of
("~" : t) -> joinPath $ homeDir : t
_ -> s
また、do記法は複雑な場合のみです。単純な 2 ライナーに do 表記を使用すると、ほぼ確実にfmap
/ <$>
/ >>=
/ >=>
/またはおよびliftM2
の他の関数の適用に還元できます。Control.Monad
Control.Applicative
別のバージョンは次のとおりです。
import Control.Applicative ((<$>))
import System.Directory (getHomeDirectory)
import System.FilePath (joinPath, splitPath)
getFullPath s = case splitPath s of
"~/" : t -> joinPath . (: t) <$> getHomeDirectory
_ -> return s
main = getFullPath "~/foo" >>= print
これは、さらにモジュール化されていますが、読みにくいバージョンです。
import Control.Applicative ((<$>), (<*>))
import System.Directory (getHomeDirectory)
import System.FilePath (joinPath, splitPath)
main = getFullPath "~/foo" >>= print
withPathComponents f = joinPath . f . splitPath
replaceHome p ("~/" : t) = p : t
replaceHome _ s = s
getFullPath path = withPathComponents . replaceHome <$> getHomeDirectory <*> return path
Haskell の達人は、モジュール性を維持しながら読みやすさを改善するために書き直すよう招待されています :)