Search code examples
haskellrecursionmonadsmonad-transformers

How to traverse a Monad with recursion definition and collect the results?


I implement the monad transformer MaybeT.

newtype MaybeT m a =
    MaybeT { runMaybeT :: m (Maybe a) }

Then, I write a monad for backtracking.

newtype BackT m a =
    BackT { unBackT :: MaybeT m (a, BackT m a) }

Here,Back m a has recursion definition.


In my mind, there are isomorphisms.

                 unBackT
BackT m a <-------------------> MaybeT m (a, BackT m a)
            BackT(constructor)    

                              runMaybeT
MaybeT m (a, BackT m a) <------------------> m (Maybe (a, BackT m a))
                         MaybeT(constructor)

Thus, I actually get something like

m (Just(1, m (Just(2, m (Just(3, m (Just(4, Nothing))))))))

In the example given above, there are 4 computations(Monad is computation?). I need something called runBackT to collect them using [].

Thanks for the answer from @rampion , and I remove some meaningless questions.

  • What is the type of results? It should be something depending on m. (Answer: The type of results should be m a.)
  • How to collect all results? Is it possible? (Answer: Monad m a does not guarantee to have a way to get an "unwrapped" type.)
  • How to collect all arguments like 1, 2, 3, 4 in example. It's type should be [a]. Does such a BackT m a -> [a] function exist? Or, can we only get m [a]? (Answer:Only BackT m a -> m [a] might exist.)

Update

Monad can be divided into two classes: "opened monads" (like [], Maybe) and "closed monads" (like IO). The "opened monads" have functions with type m a -> b to open them. e.g.

showMaybe :: (Show a) => Maybe a -> String
showMaybe mx = case mx of
    Nothing -> "Nothing"
    Just x -> show x
  1. How to implement (Monad m, Show m) => BackT m a -> [String]?
  2. More generally, Monad m => (m a -> b) -> BackT m a -> [b]?
  3. Under what conditions, does Monad m => BackT m a -> [m a] exist? BackT m a sequences computations m a recursive by cross-recursive definition. How to change it into iteration [m a]? If it exists, how to implement it? We can map m a -> b to [m a], and question (2) will be solved.
  4. Monad m => (m a -> a) -> BackT m a -> m [a]? Just wrap the result of question(2) by constructor m.

Therefore, the key point is question (3).

The most difficult part for me is recursion definition of BackT m a. I'd appreciate it if you could show the implement or share some advice.

Answers only for question (3) is OK.


Update

Thanks for comments from @rampion, the ListT from list-t package answered my questions.


Solution

  • How to collect all arguments like 1, 2, 3, 4 in example. It's type should be [a]. Does such a BackT m a -> [a] function exist? Or we can only get m [a]?

    Think of this the other way around first.

    We can certainly get a BackT m a value for any Monad m:

    Prelude> emptyBackT = BackT (MaybeT (return Nothing))
    Prelude> :t emptyBackT
    emptyBackT :: Monad m => BackT m a
    

    And with the power of fmap, we can convert any m a to a BackT m a for any Functor m:

    Prelude> lift ma = BackT (MaybeT (fmap (\a -> Just (a, emptyBackT)) ma))
    Prelude> :t lift
    lift :: Monad m => m a -> BackT m a
    

    So if we had a way to convert any BackT m a -> [a], we could combine that with lift to get m a -> [a] for any Functor m!

    But we know we can't do that in Haskell. Some functors (like [] or Maybe) unwrapped, but there's others (like IO) that can't.

    So runBackT needs to have type BackT m a -> m [a].

    As for implementation, here's some leading questions.

    You've got an isomorphism from BackT m a to m (Maybe (a, BackT m a)), so

    • Assuming runBackT :: BackT m a -> m [a] were already implemented, could you implement consBackT :: a -> BackT m a -> m [a]?
    • Assuming runBackT :: BackT m a -> m [a] were already implemented, could you implement unwrapBackT :: Maybe (a, BackT m a) -> m [a]?
    • Assuming unwrapBackT :: Maybe (a, BackT m a) -> m [a] were already implemented, could you implement innerToList :: m (Maybe (a, BackT m a)) -> m [a]?

    (Hint: the types I've used in the leading questions are incomplete)