Haskell — Как я могу использовать чистые функции внутри функций ввода-вывода?

Как я могу использовать чистые функции внутри функций ввода-вывода? :-/

Например: я читаю файл (функция ввода-вывода) и хочу проанализировать его контекст, строку, используя чистую функцию с ссылочной прозрачностью.

Кажется, что такие миры, чистые функции и функции ввода-вывода, разделены. Как я могу соединить их?


person Juan Carlos Kuri Pinto    schedule 17.02.2013    source источник
comment
какие учебники вы читали - каждое введение в ввод-вывод в haskell касается вашего вопроса. Решение состоит в том, чтобы создать IO Monad.   -  person epsilonhalbe    schedule 17.02.2013
comment
Спасибо. Я решил проблему. Смотрите ниже:   -  person Juan Carlos Kuri Pinto    schedule 17.02.2013


Ответы (4)


Самый простой способ — использовать fmap, который имеет следующий тип:

fmap :: (Functor f) => (a -> b) -> f a -> f b

IO реализует Functor, а это значит, что мы можем специализировать вышеупомянутый тип, заменив IO на f, чтобы получить:

fmap :: (a -> b) -> IO a -> IO b

Другими словами, мы берем некоторую функцию, которая преобразует as в bs, и используем ее для изменения результата действия IO. Например:

getLine :: IO String

>>> getLine
Test<Enter>
Test
>>> fmap (map toUpper) getLine
Test<Enter>
TEST

Что там только что произошло? Ну, map toUpper имеет тип:

map toUpper :: String -> String

Он принимает String в качестве аргумента и возвращает 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, or
  • do обозначение

Это действительно зависит от вас, какой из них вы предпочитаете. Я лично рекомендую fmap.

person Gabriel Gonzalez    schedule 17.02.2013
comment
Разве это не объясняет, как извлечь результат действия ввода-вывода, а не как вызвать чистую функцию из действия ввода-вывода? - person David Given; 11.06.2015

Алекс Хорсман помог мне. Он сказал:

«Возможно, я неправильно понимаю, но это звучит довольно просто? 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)
}
person Juan Carlos Kuri Pinto    schedule 17.02.2013

Вы также можете рассмотреть функцию liftM из Control.Monad.
Небольшой пример в помощь (запустите его в ghci, так как вы находитесь под IO Monad)

$ 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 
person zurgl    schedule 17.02.2013

Фактический ответ выглядит следующим образом:

main = do
  val <- return (purefunc ...arguments...)
  ...more..actions...

return заключает его в соответствующую монаду, чтобы do мог назначить его val.

person David Given    schedule 11.06.2015