Я пытаюсь сложить монады IO и Maybe, но либо я недостаточно хорошо понимаю трансформеры монад, либо это невозможно с использованием трансформеров. Может ли кто-нибудь помочь мне понять это?
f :: String -> Maybe String
main :: IO ()
main = do
input <- getLine -- IO String
output <- f input -- Maybe String (Can't extract because it is IO do block)
writeFile "out.txt" output -- gives error because writeFile expects output :: String
В приведенном выше упрощенном примере у меня есть функция f, которая возвращает Maybe String, и я хотел бы иметь аккуратный способ извлечения этого в блоке IO do. Я старался
f :: String -> MaybeT IO String
main :: IO ()
main = do
input <- getLine -- IO String
output <- runMaybeT (f input) -- Extracts output :: Maybe String instead of String
writeFile "out.txt" output -- gives error because writeFile expects output :: String
что позволяет мне извлечь Maybe String во второй строке блока do, но мне нужно извлечь из него строку. Есть ли способ сделать это без использования case?
@Carl Я искал использование двух типов монад в блоке do (что, как я понимаю, невозможно) и нашел несколько тем, предлагающих использование lift и преобразователей монад. Я мало что знал о преобразователях монад, поэтому воспринял это как момент обучения. Все еще не уверен, что это правильно использовать здесь.
Итак, что вы хотите, чтобы произошло, когда f вернется Nothing?
Я на самом деле не уверен, что было бы лучшим способом. Я мог бы выдать ошибку и не писать в файл, и в этом случае мне пришлось бы использовать оператор case, так что я думаю, это как бы отвечает на мой вопрос?
Стоит подчеркнуть одну деталь: <- в блоке do не извлекает буквально значение из монады. Скорее, он просто позволяет вам работать с как будто, и вы можете его извлечь. Например, если у вас есть x <- m в блоке выполнения Maybe, вы можете использовать остальную часть блока выполнения, чтобы указать, что будет сделано с помощью xесли m не Nothing. Если m окажется Nothing, а возможность останется, то работать будет не с x, и соответственно отказ будет распространяться через >>=.
Верно, значит, если x <- m в блоке Maybedo есть Nothing, это как бы "короткое замыкание" всего?
@Билентор Да. Если m есть Nothing, все остальное в блоке игнорируется, и результатом всего этого является Nothing.
Связано: stackoverflow.com/questions/32579133/…





Давайте на мгновение остановимся на вашем первом фрагменте. Если f input является Maybe String, и вы хотите передать его результат в writeFile "out.txt", который принимает String, вам нужно иметь дело с возможностью f input быть Nothing. Вам не нужно буквально использовать case-statement. Например:
maybe из Прелюдии — разбор случая, упакованный как функция;
fromMaybe from Data.Maybe позволяет легко указать значение по умолчанию, если это имеет смысл для вашего варианта использования;
traverse_ и for_ из Data.Foldable можно использовать для молчаливого игнорирования Nothing-ности:
for_ (f input) (writeFile "out.txt") -- Does nothing if `f input` is `Nothing`.
Тем не менее, независимо от того, что вы решите сделать, это будет связано с каким-то образом Nothing.
Что касается MaybeT, здесь вам не нужны преобразователи монад. MaybeT IO предназначен для случаев, когда вам нужно что-то вроде Maybe вычислений, в которое вы также можете включить IO вычисления. Если f :: String -> Maybe String уже делает то, что вы хотите, вам не нужно добавлять к нему базовый слой IO.
Спасибо, что объяснили это. Я не знал о fromMaybe, что действительно имеет смысл в моей проблеме.
@Карл Хорошо заметил. Я добавил это к ответу; Благодарю.
@Carl omg DUH traverse_ за это. Я всегда изобретал свой собственный комбинатор, который делает то же самое. Нужно помнить, что Maybe — это «коллекция». Спасибо.
Почему вы потянулись к преобразователям монад здесь?