
On 1/17/11 12:22 PM, Tyson Whitehead wrote:
On January 15, 2011 22:43:32 Jan-Willem Maessen wrote:
For example, I find it relatively easy to understand>>= in the continuation monad, but have to spend a long time puzzling my way through join.
It's not that bad once you get used to thinking of it. Join simply merges an inner computation into an outer one.
I've been doing a lot more of that lately, or rather _not_ doing it. In particular, there's often a desire when working in monads like IO to do some computation now (and share it) and then do some computation later (possibly many times). The natural way of doing this is to have your function return IO(IO X) where the outer IO is run now and the inner IO is run later. If you want to do both of them now you just call join. One example where this is especially helpful is when dealing with file handling based on commandline flags. In most programs we'd like to run the commandline sanity checks as early as possible so that we can fail fast, but the actual file manipulation can happen much later and far away from main. In the conventional (>>=) style of thinking ---often seen in imperative languages--- we'd have to do something like open the files early and then pass filehandles throughout the program (or worse, pass the file names throughout the program and open/close the files repeatedly). This is not only against what we'd like to do, it's also unsightly, error prone, and a maintenence nightmare. But once we switch over to a join style of thinking, the IO(IO X) approach becomes obvious. We simply check the flags now and return a thunk that will manipulate the file later. We have to pass the thunk around, but the action encapsulates whatever we want to do with the file rather than being the file itself. An added benefit of this style is that it allows us to keep the code that verifies commandline flags close by the code that makes use of that information, instead of smearing it across the whole program and loosing track of the interrelatedness. -- Live well, ~wren