
I think you'll find it useful to be explicit about what you are relying on.
Yes, good point.
For example I think that if your program contains two calls unsafePerformIO e1 unsafePerformIO e2 then * You don't care about the *order* in which these are performed
Right, the order is not important.
* You do care that they are both performed separately, even if e1=e2. That is, you do not want CSE (common sub-expression)
Right.
* I'm not sure whether you care if they are not performed at all. E.g. if you have let x = unsafePerformIO e in True
I do not care whether or not the action is performed. Even more so, if the whole evaluation strategy is not lazy but speculatively eager or something the like, this does not change the semantics of our programs.
* I'm not sure if you care whether they are performed once or many times. E.g. f = \x. x + unsafePerformIO (h z) If the (h z) doesn't mention 'x', GHC will transform this to v = unsafePerformIO (h z) f = \x. x+v So now the effect happens once only, rather than once per call of f. Again you can control this by adding an articifical dependency on x.
This is a very good example. The effect has to happen once per call to f. So what do you mean by "adding an artificial dependency"? Would for this example something like the following be enough? f = \x. x + myEffect x myEffect _ = unsafePerformIO (h z)
Adding these artificial dependencies may amount to exactly the kind of effect analysis that Joesef suggests.
Yes such a determinism analysis is a good idea to optimize functional logic programs and we have already done work in that direction. But I would not like to turn those programs into a monad which we cannot prove to be deterministic. Your hint of "adding artificial dependencies" sounds more promising. The hints and suggestions I got in this thread are already very helpful, thanks for that! Bernd