Writer определенно то, что вы хотите здесь;вы можете избежать "разоблачения" Writer
извне, потратив немного больше накладных расходов на сами определения:
foo :: [String]
foo = execWriter $ do
tell otherList1
tell otherList2
tell otherList3
otherList1 :: [String]
otherList1 = execWriter $ do
...
т.е. вы можете оставить использование Writer локальным для каждого определения, но выОберните каждый список, который вы хотите использовать в качестве «источника» в tell
.Главное здесь - использовать execWriter
, который отбрасывает элемент результата кортежа (он идентичен snd . runWriter
).
Однако, если у вас много таких определений, я бы порекомендовал простоиспользование Writer напрямую и только execWriter
в том месте, где вы хотите получить комбинированный результат;Вы можете сделать типы немного чище, определив синоним, как type Foo = Writer [String]
.
Я не уверен, каким преимуществом будет создание вашей собственной монады создания списка;в конечном итоге он будет идентичен Writer [String]
.
Монада списка действительно не имеет отношения к тому, что вы хотите здесь сделать.
Что касается определения вашей собственной монады написания спискаидет, это довольно просто:
data ListWriter a = ListWriter a [String]
runListWriter :: ListWriter a -> (a, [String])
runListWriter (ListWriter a xs) = (a, xs)
execListWriter :: ListWriter a -> [String]
execListWriter = snd . runListWriter
instance Monad ListWriter where
return a = ListWriter a []
ListWriter a xs >>= f = ListWriter b (xs ++ ys)
where ListWriter b ys = f a
Единственная сложная часть - (>>=)
, где мы должны взять только часть значения левого аргумента, передать его в аргумент правой руки, разобрать его,а затем объедините два списка внутри, обернув его обратно с результатом правой части.