Poperton
Poperton

Reputation: 1796

Understanding this simple Reader monad example

I'm trying to understand the Reader monad, and I'm following this answer https://stackoverflow.com/a/14179721/10116440 which has this example:

 import Control.Monad.Reader

 data GameState = NotOver | FirstPlayerWin | SecondPlayerWin | Tie

 data Game position
   = Game {
           getNext :: position -> [position],
           getState :: position -> GameState
          }

 getNext' :: position -> Reader (Game position) [position]
 getNext' position
   = do game <- ask
        return $ getNext game position

 getState' :: position -> Reader (Game position) GameState
 getState' position
   = do game <- ask
        return $ getState game position


 negamax :: Double -> position -> Reader (Game position) Double
 negamax color position
     = do state <- getState' position 
          case state of
             FirstPlayerWin -> return color
             SecondPlayerWin -> return $ negate color
             Tie -> return 0
             NotOver -> do possible <- getNext' position
                           values <- mapM ((liftM negate) . negamax (negate color)) possible
                           return $ maximum values

The first thing I don't understand is return $ getNext game position. getNext, when applied to a game, returns position -> [position]. This, when applied to position, returns [position]. So the line return $ getNext game position should returng something other than Reader (Game position) [position], I don't know which this something is, because I don't know how the return is defined for the Reader monad.

Anyways, which "instance" of the Game will ask return? I don't understand, there's none in this code. Also, runReader is never called, so what are the Reader results of getNext, getState and negamax used for?

Could someone complete this example with how would runReader actually be run in this code, and which game instance is returned by ask?

Also, why Reader env a and not simply Reader a?

Upvotes: 1

Views: 320

Answers (1)

radrow
radrow

Reputation: 7159

There is a plenty of questions in your post – this is quite against StackOverflow rules. Nevertheless, I will answer them one by one:

Why return $ getNext game position

Indeed, getNext game position returns something of type [position]. Now take a look at the return function — its type is Monad m => a -> m a. In this case m is Reader (Game position) (not Reader (Game position) a, nor just Reader! Reader itself isn't a Monad. Reader something is). If you instantiate a to [position] (as applied) you will get Reader (Game position) [position] as declared.

Which "instance" of the Game will ask return?

ask is a utility coming from Control.Monad.Reader. Generally speaking, Reader is a monad that handles some environment under the hood. ask :: Reader env env is an action that just results in that environment. For example

runReader ask 123 == 123

So, answering your question, it will return the currently handled game that is hidden under the Reader's coat.

Could someone complete this example

I don't know what do you mean by "complete" here, but sample usage of runReader:

runReader (negamax 1.0 123) Game{getNext = \x -> [x], getState = \x -> NotOver}

Why Reader env a and not simply Reader a

Because Reader env describes a computation with a "background" value of type env. This would make no sense without env – the point of Reader is to have that value and its type needs to be known. If you don't like writing it every time in your case you may write

type GameMonad position = Reader (Game position)

Upvotes: 3

Related Questions