Хаскелл: конкатенацию двух строк ИО
Сегодня я попытался соединить две строки ввода-вывода и не смог заставить их работать.
Итак, проблема в следующем. Предположим, что у нас есть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
, которая выполняет эту операцию для функции только с одним аргументом. В качестве альтернативы, как указывали другие, вы можно использовать экземплярIO
SApplicative
вControl.Applicative
и использовать аналогичную функциюliftA2
.Applicative
s имеют сходное поведение с универсальнымиMonad
s в определенных контекстах, и причина этого заключается в том, что они математически очень похожи. На самом деле, для каждого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
. Это не струна сам.