
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 7/30/10 06:06 , Kevin Jardine wrote:
I think that we are having a terminology confusion here. For me, a pure function is one that does not operate inside a monad. Eg. ++, map, etc.
It was at one point my belief that although code in monads could call pure functions, code in pure functions could not call functions that operated inside a monad.
I was then introduced to functions such as execState and unsafePerformIO which appear to prove that my original belief was false.
Currently I am in a state of deep confusion, but that is OK, because it means that I am learning something new!
A monad is just a wrapper that lets you take an action of some kind whenever the wrapped value is operated on. "Pure" means "referentially transparent"; that is, it should always be possible to substitute an expression for its expansion without changing its meaning. Now, certain specific monads (IO, ST, STM) are used specifically for operations that are *not* referentially transparent. Those operations are therefore confined to occurring only within the monad wrapper; ST allows you to extract a referentially transparent value (although it's up to the programmer to enforce that, and the only consequences for violation are potential odd program behaviors), the others do not without doing evil things. *** Eye-bleedy ahead; skip the next paragraph if you are in over your head. *** In the case of ST and STM, it is possible to pull values back out; in the case of ST, this means that non-referentially-transparent operations can take place "behind the curtain" as long as what emerges from the curtain is the same as would happen with a referentially transparent version (this is used when it's more efficient to alter values in place than to produce new values), while STM operations can only be extracted to IO (STM is in some sense an extension of IO) and IO operations can only be extracted by running the program or using unsafePerformIO (or its cousins unsafeInterleaveIO and unsafeIOtoST/unsafeSTtoIO), which are labeled "unsafe" specifically because they're exposing non-referentially-transparent operations which are therefore capable of causing indeterminate program behavior. *** resuming the flow *** The majority of monads (State, Writer, Reader, etc.) are entirely referentially transparent in their workings; in these cases, the wrapper is used simply to add a "hook" that is itself referentially transparent. The three mentioned above are all quite similar, in that the "hook" just carries a second value along and the monad definition includes functions that can operate on that value (get, gets, put, modify; tell; ask, asks, local). Other referentially transparent monads are used to provide controllable modification of control flow: Maybe and (Either a) let you short-circuit evaluation based on a notion of "failure"; list aka [] lets you operate on values "in parallel", with backtracking when a branch fails. Cont is the ultimate expression of this, in effect allowing the "hook" to be evaluated at any time by the wrapped operation; as such, it's worth studying, but it will probably warp your brain a bit. (It's possible to derive any of the referentially transparent monads from Cont.) The distinction between these two classes, btw, lies in whether the "hook" allows things to escape. In the case of ST, IO, and STM, the "hook" carries around an existentially qualified type, which by definition cannot be given a type outside of the wrapper. (Think of it this way: it's "existentially qualified" because its existence is qualified to only apply within the wrapper.) *** more eye-bleedy ahead *** In many IO implementations, IO is just ST with a magic value that can neither be created nor modified nor destroyed, simply passed around. The value is meaningless (and, in ghc, at least, nonexistent!); only its type is significant, because the type prevents anything using it from escaping. The other half of this trick is that operations in IO quietly "use" (by reference) this value, so that they are actually partially applied functions; this is why we refer to "IO actions". An "action" in this case is simply a partially applied function which is waiting for the magic (non-)value to be injected into it before it can produce a value. In effect, it's a baton passed between "actions" to insure that they take place in sequence. And this is why the "unsafe" functions are unsafe; they allow violation of the sequence enforced by the baton. unsafePerformIO goes behind the runtime's back to pull a copy of the baton out of the guts of the runtime and feeds it to an I/O action; unsafeInterleaveIO clones the baton(!); unsafeIOtoST doesn't actually do anything other than hide the baton, but the only thing you can do with it then is pass it to unsafeSTtoIO - --- which is really unsafePerformIO under the covers. (The purpose of those two functions is that ST's mutable arrays are identical to IO's mutable arrays, and the functions allow one to be converted to the other. - -- brandon s. allbery [linux,solaris,freebsd,perl] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.10 (Darwin) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAkxS9JcACgkQIn7hlCsL25VFsQCguHtvIHO0EXHUcnVyC4sM+B0u /oYAoNcQYL8o/0CveKh4imLIMa8ATk9D =Li/v -----END PGP SIGNATURE-----