1

I have the following code

newtype State s a = State { runState :: s -> (s,a) }    
evalState :: State s a -> s -> a
evalState sa s = snd $ runState sa s

instance Functor (State s) where
  fmap f sa = State $ \s ->
    let (s',a) = runState sa s in
      (s',f a)

instance Applicative (State s) where
  pure a = State $ \s -> (s,a)

  sf <*> sa = State $ \s ->
    let (s',f) = runState sf s
        (s'',a) = runState sa s' in
      (s'', f a)

instance Monad (State s) where
  sa >>= k = State $ \s ->
    let (s',a) = runState sa s in
      runState (k a) s'

get :: State s s
get = State $ \s -> (s,s)

set :: s -> State s ()
set s = State $ \_ -> (s,())

bar (acc,n) = if n <= 0
              then return ()
              else
              set (n*acc,n-1)

f x = factLoop

factLoop =  get >>= bar >>= f

And

 runState factLoop (1,7)

gives ((5040,0),())

I'm trying to write the function

 factLoop =  get >>= bar >>= f

using the do notation

I tried

 factLoop' =  do
                (x,y) <- get 
                h <-  bar (x,y) 
                return ( f h) 

But that does not give the correct type which should be State (Int, Int) ()

Any idea ?

Thanks!

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Tomer
  • 1,159
  • 7
  • 15
  • 2
    Get in the habit of writing explicit type signatures for your functions. It will help you see that `f h` has the type `State (Integer, Integer) b` that `factLoop` has, and therefore you don't need `return`. – chepner Mar 19 '20 at 11:14
  • [BTW](https://pastebin.com/qtT0XDAn). shorter, clearer, pseudocode -- not a valid Haskell code. :) – Will Ness Mar 20 '20 at 15:59

2 Answers2

7

Just remove the return on the final line :

factLoop' =  do
                (x,y) <- get 
                h <-  bar (x,y) 
                f h

There is no return in your original code, so there should be none in the do notation version either. do notation simply "translates" uses of >>=, as you've already done.

Robin Zigmond
  • 17,805
  • 2
  • 23
  • 34
1

>>= is infixl 1 (left-associating binary operator), so what you really have is

f x = factLoop

factLoop =  get >>= bar >>= f
         =  (get >>= bar) >>= f
         =  (get >>= bar) >>= (\x -> f x)
         = do { x <- (get >>= bar)
              ; f x }
         = do { _ <- (get >>= bar)
              ; factLoop }
         = do { _ <- (get >>= (\x -> bar x))
              ; factLoop }
         = do { _ <- do { x <- get 
                        ; bar x }
              ; factLoop }
         = do { x <- get 
              ; _ <- bar x 
              ; factLoop }

the last one is because of the monad associativity law ("Kleisli composition forms a category").

Doing this in the principled way you don't need to guess. After a little while you get a feeling for it of course, but until you do, being formal helps.

Will Ness
  • 70,110
  • 9
  • 98
  • 181