Я пытаюсь понять, как объединить traverseOf
с >>=
таким образом, чтобы было возможно следующее.
TLDR; Простым примером на чистом Haskell было бы что-то вроде этого, но с использованием линз глубоко внутри структуры данных.
λ> fmap concat $ mapM ((return :: a -> IO a) . const ["he", "he"]) ["foo", "bar", "baz"]
["he","he","he","he","he","he"]
Вот длинное объяснение с примерами
data Foo = Foo [Bar] deriving Show
data Bar = Baz | Qux Int [String] deriving Show
makePrisms ''Foo
makePrisms ''Bar
items :: [Foo]
items = [Foo [Baz], Foo [Qux 1 ["hello", "world"], Baz]]
-- Simple replacement with a constant value
constReplace :: [Foo]
constReplace = over (traverse._Foo.traverse._Qux._2.traverse) (const "hehe") items
-- λ> constReplace
-- [Foo [Baz],Foo [Qux 1 ["hehe","hehe"],Baz]]
-- Doing IO in order to fetch the new value. This could be replacing file names
-- with the String contents of the files.
ioReplace :: IO [Foo]
ioReplace = (traverse._Foo.traverse._Qux._2.traverse) (return . const "hehe") items
-- λ> ioReplace
-- [Foo [Baz],Foo [Qux 1 ["hehe","hehe"],Baz]]
-- Replacing a single value with a list and concatenating the results via bind
concatReplace :: [Foo]
concatReplace = over (traverse._Foo.traverse._Qux._2) (>>= const ["he", "he"]) items
-- λ> concatReplace
-- [Foo [Baz],Foo [Qux 1 ["he","he","he","he"],Baz]]
-- Same as the previous example, but the list comes from an IO action
concatIoReplace :: IO [Foo]
concatIoReplace = (traverse._Foo.traverse._Qux._2) (return . (>>= const ["he", "he"])) items
-- λ> concatIoReplace
-- [Foo [Baz],Foo [Qux 1 ["he","he","he","he"],Baz]]
Теперь в последнем примере проблема заключается в том, что я немного схитрил, изменив применяемую функцию. В concatReplace
я смог использовать >>=
(спасибо полезным ребятам на канале #haskell-lens
) для реализации функций, подобных concatMap
. Но в моем реальном коде у меня есть функция String -> IO [String]
, которая будет выглядеть примерно так
correctConcatIo :: IO [Foo]
correctConcatIo = (traverse._Foo.traverse._Qux._2) (>>= (return . const ["he", "he"])) items
Но этот пример больше не проверяет тип. Что мне нужно, так это собрать логику из ioReplace
и concatReplace
таким образом, чтобы я мог применить функцию с типом String -> IO [String]
к структуре данных, содержащей [String]
.