Хаскелл: конкатенацию двух строк ИО
Сегодня я попытался соединить две строки ввода-вывода и не смог заставить их работать.
Итак, проблема в следующем. Предположим, что у нас естьs1 :: IO String и s1 :: IO String. Как реализовать функцию (+++) :: IO String -> IO String -> IO String, которая работает точно так же, как (++) :: [a] -> [a] -> [a], но для строки ввода-вывода?
И более общий вопрос заключается в том, как реализовать более общую функцию (+++) :: IO a -> IO a -> IO a? Или, может быть, даже более общее? Я не очень хорошо разбираюсь в функторах и монадах.
Я буду признателен за любую помощь.
4 ответа:
Вы можете использовать
liftM2изControl.Monad:liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c > :t liftM2 (++) liftM2 (++) :: Monad m => m [a] -> m [a] -> m [a]В качестве альтернативы можно использовать нотацию
do:(+++) :: Monad m => m [a] -> m [a] -> m [a] ms1 +++ ms2 = do s1 <- ms1 s2 <- ms2 return $ s1 ++ s2
Оба они эквивалентны. Фактически, определение для
liftM2реализуется какliftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c liftM2 f m1 m2 = do val1 <- m1 val2 <- m2 return $ f val1 val2Очень просто! Все, что он делает, - это извлекает значения из двух монадических действий и применяет к ним функцию из 2 аргументов. Это относится к функции
Вы можете заметить, что универсальныеliftM, которая выполняет эту операцию для функции только с одним аргументом. В качестве альтернативы, как указывали другие, вы можно использовать экземплярIOSApplicativeвControl.Applicativeи использовать аналогичную функциюliftA2.Applicatives имеют сходное поведение с универсальнымиMonads в определенных контекстах, и причина этого заключается в том, что они математически очень похожи. На самом деле, для каждогоMonad, Вы можете сделатьApplicativeиз него. Следовательно, вы также можете сделатьFunctorиз каждогоApplicative. Есть много людей, взволнованных предложением функтор-Аппликатив-Монада , которое было вокруг на некоторое время, и, наконец, будет реализован в предстоящей версии GHC. Они составляют очень естественную иерархиюFunctor > Applicative > Monad.
import Control.Applicative (liftA2) (+++) :: Applicative f => f [a] -> f [a] -> f [a] (+++) = liftA2 (++)Теперь в GHCI
>> getLine +++ getLine Hello <ENTER> World!<ENTER> Hello World!
Не делай этого, это плохая идея. Конкатенация строк-это чисто функциональная операция, нет никаких оснований для ее использования в монадеРеализовать функцию
(+++)... который работает точно так же, как(++) :: [a] -> [a] -> [a], но дляIO String?IO. За исключением того места, где вам нужен результат – который будет где-то в середине какой-то другой ИО, я полагаю. Ну, тогда просто используйтеdo-нотацию, чтобы привязать строки чтения к именам переменных, и используйте обычные(++)на них!do print "Now start obtaining strings..." somePreliminaryActions someMoreIOStuff s1 <- getS1 s2 <- getS2 yetMoreIO useConcat'dStrings (s1 ++ s2) print "Done."Это хорошо, чтобы сделать это более компактным, написав
s12 <- liftA2 (++) getS1 getS2. Но я бы сделал это прямо на месте, а не определял его отдельно.Для более длительных операций вы, конечно, можете определить отдельное именованное действие, но оно должно быть каким-то образом значимым.
Вы не должны думать об объектах
IO Stringкак о "IO-строках". Они не являются таковыми, как[Int]не являются "целыми числами списка". Объект типаIO String- это действие, которое, будучи произведено, может предоставить объектStringв монадеIO. Это не струна сам.