Nested State - Recommended Idiomatic Approach?

2 questions:

  1. when trying to generate 2 lists of random numbers and shuffling them, I end up with nested state. I then have to call evalState twice. Is there any way I can just call once to evaluate the whole expression, or flatten the nested state (somehow doubtful)

  2. a slight tangent: I create 2 lists with replicateM. Is there anyway to create 2 infinite lists, and do a take depth when the lists are combined?

-- generate a queue of randomly sized men
ys :: State StdGen [Float]
ys = replicateM depth $ state $ normal' (179,6)

-- women with random heights
xs :: State StdGen [Float]
xs = replicateM depth $ state $ normal' (166,6)

-- no shuffle
xys :: State StdGen [Float]
xys = (\ xs ys -> xs ++ ys) <$> xs <*> ys

-- nested state. Anyway to call evalState once, rather than twice? Or Flatten
xys' :: State StdGen ( State StdGen [Float] )
xys' = (\ xs ys -> shuffle $ xs ++ ys) <$> xs <*> ys

How about

xys' = do
    xs' <- xs
    ys' <- ys
    shuffle (xs' ++ ys')

In general I recommend using do notation rather than <$>/<*> unless you are really familiar with the latter, because do notation makes it much clearer what’s going on.

3 Likes

Wow, this works a treat!

Yeh, I fmap as much as possible - obviously too much : )

fmap seems simpler to me.

1 Like

If you want to use operators rather than do-notation you can write it like this:

xys' = xs >>= \xs' -> ys >>= \ys' -> shuffle (xs' ++ ys')
-- or
xys' = join $ (\ xs' ys' -> shuffle $ xs' ++ ys') <$> xs <*> ys

But do-notation is probably easier to read in this case.

2 Likes

This is so cool!

It’s exactly what I was looking for:

join :: Monad m => m (m a) -> m a

Super straightforward to understand.

//

I think fmap is more straightforward than Monad - iirc, that is actually probably literally true…

The do notation is definitely nice syntax, but I never fully (100%) got comfortable with the semantics I suppose.