IO 関数内で純粋関数を使用するにはどうすればよいですか? :-/
例: ファイル (IO 関数) を読み取っていて、参照透過性を持つ純粋な関数を使用して、そのコンテキスト (文字列) を解析したいと考えています。
純粋関数とIO関数という世界が分かれているようです。どうすればそれらを橋渡しできますか?
IO 関数内で純粋関数を使用するにはどうすればよいですか? :-/
例: ファイル (IO 関数) を読み取っていて、参照透過性を持つ純粋な関数を使用して、そのコンテキスト (文字列) を解析したいと考えています。
純粋関数とIO関数という世界が分かれているようです。どうすればそれらを橋渡しできますか?
最も簡単な方法はfmap
、次の型を持つ を使用することです。
fmap :: (Functor f) => (a -> b) -> f a -> f b
IO
Functor
これは、 for を代入IO
しf
て取得することにより、上記の型を特殊化できることを意味します。
fmap :: (a -> b) -> IO a -> IO b
言い換えれば、a
s をb
s に変換する関数を取り、それを使用してアクションの結果を変更しIO
ます。例えば:
getLine :: IO String
>>> getLine
Test<Enter>
Test
>>> fmap (map toUpper) getLine
Test<Enter>
TEST
そこで何が起こったのですか?さて、map toUpper
タイプがあります:
map toUpper :: String -> String
引数としてa を取り、結果として aString
を返しますString
。具体的には、文字列全体を大文字にします。
では、次のタイプを見てみましょうfmap (map toUpper)
。
fmap (map toUpper) :: IO String -> IO String
関数をアップグレードして、IO
値を操作できるようにしました。アクションの結果を変換してIO
、大文字の文字列を返します。
do
記法を使用してこれを実装することもできます。
getUpperCase :: IO String
getUpperCase = do
str <- getLine
return (map toUpper str)
>>> getUpperCase
Test<Enter>
TEST
すべてのモナドには次のプロパティがあることがわかります。
fmap f m = do
x <- m
return (f x)
つまり、いずれかの型が を実装している場合、上記の定義を使用してMonad
常に も実装できる必要があります。Functor
実際、常に をliftM
デフォルトの実装として使用できfmap
ます。
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
x <- m
return (f x)
liftM
fmap
ファンクターほど一般的ではないモナドに特殊化されていることを除いて、 と同じです。
したがって、IO
アクションの結果を変換する場合は、次のいずれかを使用できます。
fmap
、liftM
、 またdo
表記どちらを好むかは本当にあなた次第です。個人的におすすめfmap
です。
実際の答えは次のとおりです。
main = do
val <- return (purefunc ...arguments...)
...more..actions...
return
do
に割り当てることができるように、適切なモナドでラップしval
ます。
Control.Monad の liftM 関数も考えられます。
あなたを助けるための小さな例(IOモナドの下にいるので、ghciに実行してください)
$ import Control.Monad -- to emerge liftM
$ import Data.Char -- to emerge toUpper
$ :t map to Upper -- A pure function
map toUpper :: [Char] -> [Char]
$ :t liftM
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
$ liftM (map toUpper) getLine
アレックス・ホースマンが私を助けてくれました。彼は言った:
「たぶん私は誤解していますが、それはかなり単純に聞こえますか? do {x <- ioFunc; return (pureFunc x)}」
そして、私は私の問題を解決しました:
import System.IO
import Data.List
getFirstPart line Nothing = line
getFirstPart line (Just index) = fst $ splitAt index line
eliminateComment line =
getFirstPart line $ elemIndex ';' line
eliminateCarriageReturn line =
getFirstPart line $ elemIndex '\r' line
eliminateEntersAndComments :: String -> String
eliminateEntersAndComments text =
concat $ map mapFunction $ lines text
where
mapFunction = (++ " ") . eliminateCarriageReturn . eliminateComment
main = do {
contents <- readFile "../DWR-operators.txt";
return (eliminateEntersAndComments contents)
}