Domanda

I wanted to transform the folowing genEdges function in a tail recursive one.

genEdges :: Int -> Node -> IO [Edge]
genEdges n origin | n == 0 = return []
                  | otherwise =  do
                      edge <- genRandEdge origin
                      edges <- genEdges (n-1) (snd edge)
                      return $ edge : edges

I mean, the last line should be something like

return $ edge : genEdges (n-1) (snd edge)

although I know that the types of edge and genEdges (n-1) (snd edge) are different and hence this example line is incorrect.

Is that possible? If so, how should be the function? If not, why?

È stato utile?

Soluzione

It's impossible. Your tail call is really

(genRange origin) >>= (\edge -> .....)

and hence it is not recursive.

----------- Edit for updated question -----------------

Generally, if you have something like

do
  a <- foo
  bar

you can't make something tail recursive out of it. This is because this gets desugared to

foo >>= (\a -> bar)

so (>>=) (or (>>)) is the tail call.

Altri suggerimenti

EDIT

Looking at Ingo's answer he is right, you'd probably have to pass the generator into the function then you can use a pure function to get the next random number and pass the gen that it produces to the next recursive function call.

END EDIT

You can make a helper function that has an accumulator so like this. I don't have a compiler so I can't guaranteed it is exactly correct.

genEdges :: Int -> Node -> IO [Edge]
genEdges n o = genEdgesTR n o []
    where genEdgesRec n origin acc
          | n == 0  = return acc
          | otherwise   = do
            edge <- get RandEdge orgin
            genEdgesRec (n-1) (snd edge) (edge : acc)

This could probably be written with foldM or something but that is up to you to figure out.

If you end up using IO or other monad/monad transformer such as RandT (other options are passing generators and/or pregenerated infinite lists around), here is an example of using the state monad transformer to help with threading of origin:

import Control.Monad.State

data Node = Node
type Edge = ((), Node)

genRandEdge :: Node -> IO Edge
genRandEdge = undefined

genEdges :: Int -> Node -> IO [Edge]
genEdges n = evalStateT $ replicateM n $ do
        origin <- get
        edge <- liftIO $ genRandEdge origin
        put $ snd edge
        return edge

To help you with better genRandEdge to avoid IO we need more context so feel free to ask another question on how your generation approach can be improved.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top