Well, I think the best way to explain is to simply write the code.
First of all, you will want to hide too much the internal work of the monad in which you are currently working. We will do this with a type alias, but there are more powerful ways, see This chapter from Real World Haskell .
type PersonManagement = State Int
The reason for this is that you will add more things to PersonManagement later and its good practice to use black box abstraction.
Along with the definition of PersonManagement, you must expose the primitive operations that define this monad. In your case, we only have a checkmark function that looks almost the same, but with a clearer signature and a more suggestive name.
generatePersonId :: PersonManagement Int generatePersonId = do n <- get put (n+1) return n
Now all of the above should be in a separate module. In addition, we can define more complex operations, such as creating a new Person:
createPerson :: String -> PersonManagement Person createPerson name = do id <- generatePersonId return $ Person id name
By now, you probably understood that PersonManagement is a type of calculation, or a process that encapsulates the logic for communicating with Persons, and PersonManagement Person is a calculation from which we get the person object. This is very nice, but how do we actually get the people we just created and do something with them, for example, print their data on the console. Well, we need a run method that starts our process and gives us the result.
runPersonManagement :: PersonManagement a -> a runPersonManagement m = evalState m startState
RunPersonManagement launches the monad and gets the final result, performing all the side effects in the background (in your case, noting the status of Int). This uses evalState from the state monad, and it should also be in the module described above, since it knows about the internal operation of the monad. I suggested that you always want to start the person id from a fixed value identified by startState.
So, for example, if we wanted to create two people and print them on the console, the program would look something like this:
work :: PersonManagement (Person, Person) work = do john <- createPerson "John" steve <- createPerson "Steve" return (john, steve) main = do let (john, steve) = runPersonManagement work putStrLn $ show john putStrLn $ show steve
Output:
Person {id = 0, name = "John"} Person {id = 1, name = "Steve"}
Since PersonManagement is a full-fledged monad, you can also use common functions from Control.Monad . Suppose you want to create a list of faces from a list of names. Well, that only the map function is removed in the monad area - it is called mapM .
createFromNames :: [String] -> PersonManagement [Person] createFromNames names = mapM createPerson names
Using:
runPersonManagement $ createFromNames ["Alice", "Bob", "Mike"] => [ Person {id = 0, name = "Alice"}, Person {id = 1, name = "Bob"}, Person {id = 2, name = "Mike"} ]
And examples can go on.
To answer one of your questions - you work in the PersonManagement monad only when you need the services provided by this monad - in this case, the generatePersonId function or you need functions that in turn require monad primitives, such as work , which the function needs createPerson , which, in turn, must be run inside the PersonManagement monad, because it needs a counter with an individual increase. If you have, for example, a function that checks whether two people have the same data, you will not need to work inside the PersonManagement monad, and this should be a normal pure function like Person -> Person -> Bool .
To really understand how to work with monads, you just need to go through many examples. The real world of Haskell is a great start, and so Teach you Haskell .
You should also look at some libraries that use monads to see how they are created and how people use them. One great example is parsers, and parsec is a great place to start.
In addition, this paper by P. Wadler gives some very nice examples, and, of course, there are many more resources that are ready to be discovered.